Ticket #7334: trac-7334-logcontract-5.patch

File trac-7334-logcontract-5.patch, 16.3 KB (added by robert.marik, 11 years ago)
  • sage/symbolic/expression.pyx

    # HG changeset patch
    # User Robert Marik <marik@mendelu.cz>
    # Date 1265231801 -3600
    # Node ID 7e4886622664410167a506bdce7ebffa19beb0b1
    # Parent  bf84525b72903999beae77685f41a227396f87ad
    Ticket #7334 Sage cannot simplify sums of logarithms
    
    diff -r bf84525b7290 -r 7e4886622664 sage/symbolic/expression.pyx
    a b  
    27352735
    27362736    trig_expand = expand_trig
    27372737
     2738    def reduce_trig(self, var=None):
     2739        r"""
     2740        Combines products and powers of trigonometric and hyperbolic
     2741        sin's and cos's of x into those of multiples of x. It also
     2742        tries to eliminate these functions when they occur in
     2743        denominators.
     2744       
     2745        INPUT:
     2746
     2747        - ``self`` - a symbolic expression
     2748
     2749        - ``var`` - (default: None) the variable which is used for
     2750          these transformations. If not specified, all variables are
     2751          used.
     2752       
     2753        OUTPUT: a symbolic expression
     2754       
     2755        EXAMPLES::
     2756
     2757            sage: y=var('y')
     2758            sage: f=sin(x)*cos(x)^3+sin(y)^2
     2759            sage: f.reduce_trig()
     2760            1/4*sin(2*x) + 1/8*sin(4*x) - 1/2*cos(2*y) + 1/2
     2761
     2762        To reduce only the expressions involving x we use optional parameter::
     2763
     2764            sage: f.reduce_trig(x)
     2765            sin(y)^2 + 1/4*sin(2*x) + 1/8*sin(4*x)
     2766
     2767        ALIASES: :meth:`trig_reduce` and :meth:`reduce_trig` are the same
     2768        """
     2769        M = self._maxima_()
     2770        P = M.parent()
     2771        if var is None:
     2772            cmd = 'trigreduce(%s)'%(M.name())
     2773        else:
     2774            cmd = 'trigreduce(%s,%s)'%(M.name(),str(var))
     2775        ans = P(cmd)
     2776        return self.parent()(ans)
     2777
     2778    trig_reduce = reduce_trig
     2779
    27382780    ############################################################################
    27392781    # Pattern Matching
    27402782    ############################################################################
     
    53065348
    53075349           :meth:`simplify_full`, :meth:`simplify_trig`,
    53085350           :meth:`simplify_rational`, :meth:`simplify_radical`,
    5309            :meth:`simplify_factorial`
     5351           :meth:`simplify_factorial`, :meth:`simplify_log`
    53105352
    53115353        EXAMPLES::
    53125354
     
    53555397        x = x.simplify_trig()
    53565398        x = x.simplify_rational()
    53575399        x = x.simplify_radical()
     5400        x = x.simplify_log('one')
    53585401        return x
    53595402
    53605403    full_simplify = simplify_full
    53615404
    5362     def simplify_trig(self):
    5363         r"""
    5364         First expands using trig_expand, then employs the identities
    5365         `\sin(x)^2 + \cos(x)^2 = 1` and
    5366         `\cosh(x)^2 - \sin(x)^2 = 1` to simplify expressions
    5367         containing tan, sec, etc., to sin, cos, sinh, cosh.
    5368        
    5369         ALIAS: trig_simplify and simplify_trig are the same
     5405    def simplify_trig(self,expand=True):
     5406        r"""
     5407        Optionally expands and then employs the identities
     5408        `\sin(x)^2 + \cos(x)^2 = 1` and `\cosh(x)^2 - \sinh(x)^2 = 1`
     5409        to simplify expressions containing tan, sec, etc., to sin,
     5410        cos, sinh, cosh.
     5411       
     5412        INPUT:
     5413
     5414        - ``self`` - symbolic expression
     5415
     5416        - ``expand`` - (default:True) if True, expands trigonometric
     5417          and hyperbolic functions of sums of angles and of multiple
     5418          angles occurring in ``self`` first. For best results,
     5419          ``self`` should be expanded. See also :meth:`expand_trig` to
     5420          get more controls on this expansion.
     5421       
     5422        ALIAS: :meth:`trig_simplify` and :meth:`simplify_trig` are the same
    53705423       
    53715424        EXAMPLES::
    53725425       
     
    53765429            sin(x)^2 + cos(x)^2
    53775430            sage: f.simplify_trig()
    53785431            1
     5432
     5433        In some cases we do not want to expand::
     5434
     5435            sage: f=tan(3*x)
     5436            sage: f.simplify_trig()
     5437            (4*cos(x)^2 - 1)*sin(x)/(4*cos(x)^3 - 3*cos(x))
     5438            sage: f.simplify_trig(False)           
     5439            sin(3*x)/cos(3*x)
     5440       
    53795441        """
    53805442        # much better to expand first, since it often doesn't work
    53815443        # right otherwise!
    5382         return self.parent()(self._maxima_().trigexpand().trigsimp())
     5444        if expand:
     5445            return self.parent()(self._maxima_().trigexpand().trigsimp())
     5446        else:
     5447            return self.parent()(self._maxima_().trigsimp())
    53835448
    53845449    trig_simplify = simplify_trig
    53855450
    5386     def simplify_rational(self):
    5387         """
    5388         Simplify by expanding repeatedly rational expressions.
    5389        
    5390         ALIAS: rational_simplify and simplify_rational are the same
     5451    def simplify_rational(self,method='full', map=False):
     5452        r"""
     5453        Simplify rational expressions.
     5454
     5455        INPUT:
     5456
     5457        - ``self`` - symbolic expression
     5458
     5459        - ``method`` - (default: 'full') string which switches the
     5460          method for simplifications. Possible values are
     5461
     5462          - 'simple' (simplify rational functions into quotient of two
     5463            poylnomials),
     5464
     5465          - 'full' (apply repeatedly, if necessary)
     5466
     5467          - 'noexpand' (convert to commmon denominator and add)
     5468
     5469        - ``map`` - (default: False) if True, the result is an
     5470          expression whose leading operator is the same as that of the
     5471          expression ``self`` but whose subparts are the results of
     5472          applying simplifaction rules to the corresponding subparts
     5473          of the expressions.
     5474
     5475        ALIAS: :meth:`rational_simplify` and :meth:`simplify_rational`
     5476        are the same
     5477
     5478        DETAILS: We call Maxima functions ratsimp, fullratsimp and
     5479        xthru. If each part of the expression has to be simplified
     5480        separately, we use Maxima function map.
    53915481       
    53925482        EXAMPLES::
    53935483       
     
    54035493            ((x - 1)^(3/2) - sqrt(x - 1)*(x + 1))/sqrt((x - 1)*(x + 1))
    54045494            sage: f.simplify_rational()
    54055495            -2*sqrt(x - 1)/sqrt(x^2 - 1)
    5406         """
    5407         return self.parent()(self._maxima_().fullratsimp())
     5496
     5497        With ``map=True`` each term in a sum is simplified separately
     5498        and thus the resuls are shorter for functions which are
     5499        combination of rational and nonrational funtions. In the
     5500        following example, we use this option if we want not to
     5501        combine logarithm and the rational function into one
     5502        fraction::
     5503
     5504            sage: f=(x^2-1)/(x+1)-ln(x)/(x+2)
     5505            sage: f.simplify_rational()
     5506            (x^2 + x - log(x) - 2)/(x + 2)
     5507            sage: f.simplify_rational(map=True)
     5508            x - log(x)/(x + 2) - 1
     5509
     5510        With option ``method='noexpand'`` we only convert to common
     5511        denominators and add. No expansion of products is performed::
     5512
     5513            sage: f=1/(x+1)+x/(x+2)^2
     5514            sage: f.simplify_rational()
     5515            (2*x^2 + 5*x + 4)/(x^3 + 5*x^2 + 8*x + 4)
     5516            sage: f.simplify_rational(method='noexpand')
     5517            ((x + 1)*x + (x + 2)^2)/((x + 1)*(x + 2)^2)
     5518
     5519        """
     5520        self_m = self._maxima_()
     5521        if method == 'full':
     5522            maxima_method = 'fullratsimp'
     5523        elif method == 'simple':
     5524            maxima_method = 'ratsimp'
     5525        elif method == 'noexpand':
     5526            maxima_method = 'xthru'
     5527        else:
     5528            raise NotImplementedError, "unknown method, see the help for available methods"
     5529        P = self_m.parent()
     5530        self_str=self_m.str()
     5531        if map:
     5532            cmd = "if atom(%s) then %s(%s) else map(%s,%s)"%(self_str,maxima_method,self_str,maxima_method,self_str)
     5533        else:
     5534            cmd = "%s(%s)"%(maxima_method,self_m.str())
     5535        res = P(cmd)
     5536        return self.parent()(res)
    54085537
    54095538    rational_simplify = simplify_rational
    54105539
    5411     # TODO: come up with a way to intelligently wrap Maxima's way of
    5412     # fine-tuning all simplificationsrational
    5413 
    54145540    def simplify_factorial(self):
    54155541        """
    54165542        Simplify by combining expressions with factorials, and by
     
    54745600        among the components of the expression for simplifications based on
    54755601        factoring and partial fraction expansions of exponents."
    54765602       
    5477         ALIAS: radical_simplify, simplify_radical, simplify_log,
    5478         log_simplify, exp_simplify, simplify_exp are all the same
     5603        ALIAS: radical_simplify, simplify_radical, exp_simplify, simplify_exp
     5604        are all the same
    54795605       
    54805606        EXAMPLES::
    54815607       
     
    55065632        maxima.eval('domain: complex$')
    55075633        return res
    55085634
    5509     radical_simplify = simplify_log = log_simplify = simplify_radical
     5635    radical_simplify = simplify_radical
    55105636    simplify_exp = exp_simplify = simplify_radical
    55115637
     5638    def simplify_log(self,method=None):
     5639        r"""
     5640        Simplifies symbolic expression, which can contain logs.
     5641
     5642        Recursively scans the expression self, transforming
     5643        subexpressions of the form a1*log(b1) + a2*log(b2) + c into
     5644        log(b1^a1 * b2^a2) + c and simplifies inside logarithm. User
     5645        can specify, which conditions must satisfy a1 and a2 to use
     5646        this transformation in optional parameter ``method``.
     5647
     5648        INPUT:
     5649       
     5650        - ``self`` - expression to be simplified
     5651
     5652        - ``method`` - (default: None) optional, governs the condition
     5653          on a1 and a2 which must be satisfied to contract expression
     5654          a1*log(b1) + a2*log(b2). Values are
     5655         
     5656          - None (use Maxima default, integers),
     5657
     5658          - 'one' (1 and -1),
     5659
     5660          - 'ratios' (integers and fractions of integers),
     5661
     5662          - 'constants' (constants),
     5663
     5664          - 'all' (all expressions). 
     5665
     5666          See also examples below.
     5667           
     5668        DETAILS: This uses the Maxima logcontract() command. From the
     5669        Maxima documentation: "Recursively scans the expression expr,
     5670        transforming subexpressions of the form a1*log(b1) +
     5671        a2*log(b2) + c into log(ratsimp(b1^a1 * b2^a2)) + c. The user
     5672        can control which coefficients are contracted by setting the
     5673        option logconcoeffp to the name of a predicate function of one
     5674        argument. E.g. if you like to generate SQRTs, you can do
     5675        logconcoeffp:'logconfun$ logconfun(m):=featurep(m,integer) or
     5676        ratnump(m)$ . Then logcontract(1/2*log(x)); will give
     5677        log(sqrt(x))."
     5678       
     5679        ALIAS: :meth:`log_simplify` and :meth:`log_simplify` are the
     5680        same
     5681
     5682        EXAMPLES::
     5683       
     5684            sage: x,y,t=var('x y t')
     5685       
     5686        Only two first terms are contracted in the following example ,
     5687        the logarithm with coefficient 1/2 is not contracted::
     5688       
     5689            sage: f = log(x)+2*log(y)+1/2*log(t)
     5690            sage: f.simplify_log()
     5691            log(x*y^2) + 1/2*log(t)
     5692           
     5693        To contract all terms in previous example use option ``method``::
     5694       
     5695            sage: f.simplify_log(method='ratios')
     5696            log(sqrt(t)*x*y^2)
     5697
     5698        This shows that the option ``method`` from the previous call
     5699        has no influence to future calls (we changed some default
     5700        Maxima flag and have to ensure, that this flag has been
     5701        restored)::
     5702       
     5703            sage: f.simplify_log('one')
     5704            1/2*log(t) + log(x) + 2*log(y)
     5705
     5706            sage: f.simplify_log('ratios')
     5707            log(sqrt(t)*x*y^2)     
     5708
     5709            sage: f.simplify_log()
     5710            log(x*y^2) + 1/2*log(t)     
     5711
     5712        To contract terms with no coefficient (more preciselly, with
     5713        coefficients 1 and -1) use option ``method``::
     5714       
     5715            sage: f = log(x)+2*log(y)-log(t)   
     5716            sage: f.simplify_log('one')   
     5717            2*log(y) + log(x/t)
     5718
     5719        ::
     5720
     5721            sage: f = log(x)+log(y)-1/3*log((x+1))
     5722            sage: f.simplify_log()
     5723            -1/3*log(x + 1) + log(x*y)
     5724
     5725            sage: f.simplify_log('ratios')
     5726            log(x*y/(x + 1)^(1/3))
     5727
     5728        `\pi` is irrational number, to contract logarithms in the following example
     5729        we have to put ``method`` to ``constants`` or ``all``::
     5730
     5731            sage: f = log(x)+log(y)-pi*log((x+1))
     5732            sage: f.simplify_log('constants')
     5733            log(x*y/(x + 1)^pi)
     5734
     5735        x*log(9) is contracted only if ``method`` is ``all``::
     5736
     5737            sage: (x*log(9)).simplify_log()
     5738            x*log(9)
     5739            sage: (x*log(9)).simplify_log('all')
     5740            log(9^x)
     5741
     5742        TESTS::   
     5743       
     5744        This shows that the issue at trac #7344 is fixed::
     5745
     5746            sage: (log(sqrt(2)-1)+log(sqrt(2)+1)).simplify_full()
     5747            0
     5748           
     5749        AUTHORS:
     5750       
     5751        - Robert Marik (11-2009)
     5752        """
     5753        from sage.calculus.calculus import maxima
     5754        maxima.eval('domain: real$ savelogexpand:logexpand$ logexpand:false$')
     5755        if method is not None:
     5756            maxima.eval('logconcoeffp:\'logconfun$')
     5757        if method == 'ratios':
     5758            maxima.eval('logconfun(m):= featurep(m,integer) or ratnump(m)$')
     5759        elif method == 'one':
     5760            maxima.eval('logconfun(m):= is(m=1) or is(m=-1)$')
     5761        elif method == 'constants':
     5762            maxima.eval('logconfun(m):= constantp(m)$')
     5763        elif method == 'all':
     5764            maxima.eval('logconfun(m):= true$')
     5765        elif method is not None:
     5766            raise NotImplementedError, "unknown method, see the help for available methods"
     5767        res = self.parent()(self._maxima_().logcontract())
     5768        maxima.eval('domain: complex$')
     5769        if method is not None:
     5770            maxima.eval('logconcoeffp:false$')
     5771            maxima.eval('logexpand:savelogexpand$') 
     5772        return res
     5773
     5774    log_simplify = simplify_log
     5775
     5776    def expand_log(self,method='products'):
     5777        r"""
     5778        Simplifies symbolic expression, which can contain logs.
     5779
     5780        Expands logarithms of powers, logarithms of products and
     5781        logarithms of quotients.  the option ``mehotd`` tells, which
     5782        expression should be expanded.
     5783
     5784        INPUT:
     5785       
     5786        - ``self`` - expression to be simplified
     5787
     5788        - ``method`` - (default: 'product') optional, governs which
     5789          expression is expanded. Possible values are
     5790         
     5791          - 'nothing' (no expansion),
     5792
     5793          - 'powers' (log(a^r) is expanded),
     5794
     5795          - 'products' (like 'powers' and also log(a*b) are expanded),
     5796         
     5797          - 'all' (all possible expansion).
     5798
     5799          See also examples below.       
     5800       
     5801        DETAILS: This uses the Maxima simplifier and sets
     5802        ``logexpand`` option for this simplifier. From the Maxima
     5803        documentation: "Logexpand:true causes log(a^b) to become
     5804        b*log(a). If it is set to all, log(a*b) will also simplify to
     5805        log(a)+log(b). If it is set to super, then log(a/b) will also
     5806        simplify to log(a)-log(b) for rational numbers a/b,
     5807        a#1. (log(1/b), for integer b, always simplifies.) If it is
     5808        set to false, all of these simplifications will be turned
     5809        off. "
     5810       
     5811        ALIAS: :meth:`log_expand` and :meth:`expand(log)` are the same
     5812
     5813        EXAMPLES::
     5814
     5815        By default powers and products (and quotients) are expanded,
     5816        but not quotients of integers::
     5817       
     5818            sage: (log(3/4*x^pi)).log_expand()
     5819            pi*log(x) + log(3/4)
     5820
     5821        To expand also log(3/4) use ``method='all'``::
     5822
     5823            sage: (log(3/4*x^pi)).log_expand('all')
     5824            pi*log(x) + log(3) - log(4)
     5825
     5826        To expand only the power use ``method='powers'``.::
     5827
     5828            sage: (log(x^6)).log_expand('powers')
     5829            6*log(x)
     5830
     5831        The expression ``log((3*x)^6)`` is not expanded with
     5832        ``method='powers'``, since it is converted into product
     5833        first::
     5834       
     5835            sage: (log((3*x)^6)).log_expand('powers')
     5836            log(729*x^6)
     5837
     5838               
     5839        AUTHORS:
     5840       
     5841        - Robert Marik (11-2009)
     5842        """
     5843        from sage.calculus.calculus import maxima
     5844        maxima.eval('domain: real$')
     5845        if method == 'nothing':
     5846            maxima_method='false'
     5847        elif method == 'powers':
     5848            maxima_method='true'
     5849        elif method == 'products':
     5850            maxima_method='all'
     5851        elif method == 'all':
     5852            maxima_method='super'
     5853        else:
     5854            raise NotImplementedError, "unknown method, see the help for available methods"
     5855        self_m = self._maxima_()
     5856        res = self_m.ev("logexpand:%s"%maxima_method)
     5857        res = res.sage()
     5858        maxima.eval('domain: complex$')
     5859        return res   
     5860
     5861    log_expand = expand_log
     5862
    55125863
    55135864    def factor(self, dontfactor=[]):
    55145865        """