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

File trac-7334-logcontract-4.patch, 16.5 KB (added by robert.marik, 11 years ago)

Apply only this patch.

  • sage/symbolic/expression.pyx

    # HG changeset patch
    # User Robert Marik <marik@mendelu.cz>
    # Date 1258022916 -3600
    # Node ID 99865d3e27849bbcc302fa9107dcb6a2d98e5a7a
    # Parent  e4812c5582dd1bd18cb33a852b5af0109f023aba
     Ticket #7334 (Sage cannot simplify sums of logarithms) plus some enhacements related to expansion of logarithms and simplifications of rationa functions
    
    diff -r e4812c5582dd -r 99865d3e2784 sage/symbolic/expression.pyx
    a b  
    25102510
    25112511    trig_expand = expand_trig
    25122512
     2513    def reduce_trig(self, var=None):
     2514        r"""
     2515        Combines products and powers of trigonometric and hyperbolic
     2516        sin's and cos's of x into those of multiples of x. It also
     2517        tries to eliminate these functions when they occur in
     2518        denominators.
     2519       
     2520        INPUT:
     2521
     2522        - ``self`` - a symbolic expression
     2523
     2524        - ``var`` - (default: None) the variable which is used for
     2525          these transformations. If not specified, all variables are
     2526          used.
     2527       
     2528        OUTPUT: a symbolic expression
     2529       
     2530        EXAMPLES::
     2531
     2532            sage: y=var('y')
     2533            sage: f=sin(x)*cos(x)^3+sin(y)^2
     2534            sage: f.reduce_trig()
     2535            1/4*sin(2*x) + 1/8*sin(4*x) - 1/2*cos(2*y) + 1/2
     2536
     2537        To reduce only the expressions involving x we use optional parameter::
     2538
     2539            sage: f.reduce_trig(x)
     2540            sin(y)^2 + 1/4*sin(2*x) + 1/8*sin(4*x)
     2541
     2542        ALIASES: :meth:`trig_reduce` and :meth:`reduce_trig` are the same
     2543        """
     2544        M = self._maxima_()
     2545        P = M.parent()
     2546        if var is None:
     2547            cmd = 'trigreduce(%s)'%(M.name())
     2548        else:
     2549            cmd = 'trigreduce(%s,%s)'%(M.name(),str(var))
     2550        ans = P(cmd)
     2551        return self.parent()(ans)
     2552
     2553    trig_reduce = reduce_trig
     2554
    25132555    ############################################################################
    25142556    # Pattern Matching
    25152557    ############################################################################
     
    50475089
    50485090           :meth:`simplify_full`, :meth:`simplify_trig`,
    50495091           :meth:`simplify_rational`, :meth:`simplify_radical`,
    5050            :meth:`simplify_factorial`
     5092           :meth:`simplify_factorial`, :meth:`simplify_log`
    50515093
    50525094        EXAMPLES::
    50535095
     
    50965138        x = x.simplify_trig()
    50975139        x = x.simplify_rational()
    50985140        x = x.simplify_radical()
     5141        x = x.simplify_log('one')
    50995142        return x
    51005143
    51015144    full_simplify = simplify_full
    51025145
    5103     def simplify_trig(self):
    5104         r"""
    5105         First expands using trig_expand, then employs the identities
    5106         `\sin(x)^2 + \cos(x)^2 = 1` and
    5107         `\cosh(x)^2 - \sin(x)^2 = 1` to simplify expressions
    5108         containing tan, sec, etc., to sin, cos, sinh, cosh.
    5109        
    5110         ALIAS: trig_simplify and simplify_trig are the same
     5146    def simplify_trig(self,expand=True):
     5147        r"""
     5148        Optionally expands and then employs the identities
     5149        `\sin(x)^2 + \cos(x)^2 = 1` and `\cosh(x)^2 - \sinh(x)^2 = 1`
     5150        to simplify expressions containing tan, sec, etc., to sin,
     5151        cos, sinh, cosh.
     5152       
     5153        INPUT:
     5154
     5155        - ``self`` - symbolic expression
     5156
     5157        - ``expand`` - (default:True) if True, expands trigonometric
     5158          and hyperbolic functions of sums of angles and of multiple
     5159          angles occurring in ``self`` first. For best results,
     5160          ``self`` should be expanded. See also :meth:`expand_trig` to
     5161          get more controls on this expansion.
     5162       
     5163        ALIAS: :meth:`trig_simplify` and :meth:`simplify_trig` are the same
    51115164       
    51125165        EXAMPLES::
    51135166       
     
    51175170            sin(x)^2 + cos(x)^2
    51185171            sage: f.simplify_trig()
    51195172            1
     5173
     5174        In some cases we do not want to expand::
     5175
     5176            sage: f=tan(3*x)
     5177            sage: f.simplify_trig()
     5178            (4*cos(x)^2 - 1)*sin(x)/(4*cos(x)^3 - 3*cos(x))
     5179            sage: f.simplify_trig(False)           
     5180            sin(3*x)/cos(3*x)
     5181       
    51205182        """
    51215183        # much better to expand first, since it often doesn't work
    51225184        # right otherwise!
    5123         return self.parent()(self._maxima_().trigexpand().trigsimp())
     5185        if expand:
     5186            return self.parent()(self._maxima_().trigexpand().trigsimp())
     5187        else:
     5188            return self.parent()(self._maxima_().trigsimp())
    51245189
    51255190    trig_simplify = simplify_trig
    51265191
    5127     def simplify_rational(self):
    5128         """
    5129         Simplify by expanding repeatedly rational expressions.
    5130        
    5131         ALIAS: rational_simplify and simplify_rational are the same
     5192    def simplify_rational(self,method='full', map=False):
     5193        r"""
     5194        Simplify rational expressions.
     5195
     5196        INPUT:
     5197
     5198        - ``self`` - symbolic expression
     5199
     5200        - ``method`` - (default: 'full') string which switches the
     5201          method for simplifications. Possible values are
     5202
     5203          - 'simple' (simplify rational functions into quotient of two
     5204            poylnomials),
     5205
     5206          - 'full' (apply repeatedly, if necessary)
     5207
     5208          - 'noexpand' (convert to commmon denominator and add)
     5209
     5210        - ``map`` - (default: False) if True, the result is an
     5211          expression whose leading operator is the same as that of the
     5212          expression ``self`` but whose subparts are the results of
     5213          applying simplifaction rules to the corresponding subparts
     5214          of the expressions.
     5215
     5216        ALIAS: :meth:`rational_simplify` and :meth:`simplify_rational`
     5217        are the same
     5218
     5219        DETAILS: We call Maxima functions ratsimp, fullratsimp and
     5220        xthru. If each part of the expression has to be simplified
     5221        separately, we use Maxima function map.
    51325222       
    51335223        EXAMPLES::
    51345224       
     
    51445234            ((x - 1)^(3/2) - sqrt(x - 1)*(x + 1))/sqrt((x - 1)*(x + 1))
    51455235            sage: f.simplify_rational()
    51465236            -2*sqrt(x - 1)/sqrt(x^2 - 1)
    5147         """
    5148         return self.parent()(self._maxima_().fullratsimp())
     5237
     5238        With ``map=True`` each term in a sum is simplified separately
     5239        and thus the resuls are shorter for functions which are
     5240        combination of rational and nonrational funtions. In the
     5241        following example, we use this option if we want not to
     5242        combine logarithm and the rational function into one
     5243        fraction::
     5244
     5245            sage: f=(x^2-1)/(x+1)-ln(x)/(x+2)
     5246            sage: f.simplify_rational()
     5247            (x^2 + x - log(x) - 2)/(x + 2)
     5248            sage: f.simplify_rational(map=True)
     5249            x - log(x)/(x + 2) - 1
     5250
     5251        With option ``method='noexpand'`` we only convert to common
     5252        denominators and add. No expansion of products is performed::
     5253
     5254            sage: f=1/(x+1)+x/(x+2)^2
     5255            sage: f.simplify_rational()
     5256            (2*x^2 + 5*x + 4)/(x^3 + 5*x^2 + 8*x + 4)
     5257            sage: f.simplify_rational(method='noexpand')
     5258            ((x + 1)*x + (x + 2)^2)/((x + 1)*(x + 2)^2)
     5259
     5260        """
     5261        self_m = self._maxima_()
     5262        if method == 'full':
     5263            maxima_method = 'fullratsimp'
     5264        elif method == 'simple':
     5265            maxima_method = 'ratsimp'
     5266        elif method == 'noexpand':
     5267            maxima_method = 'xthru'
     5268        else:
     5269            raise NotImplementedError, "unknown method, see the help for available methods"
     5270        P = self_m.parent()
     5271        self_str=self_m.str()
     5272        if map:
     5273            cmd = "if atom(%s) then %s(%s) else map(%s,%s)"%(self_str,maxima_method,self_str,maxima_method,self_str)
     5274        else:
     5275            cmd = "%s(%s)"%(maxima_method,self_m.str())
     5276        res = P(cmd)
     5277        return self.parent()(res)
    51495278
    51505279    rational_simplify = simplify_rational
    51515280
    5152     # TODO: come up with a way to intelligently wrap Maxima's way of
    5153     # fine-tuning all simplificationsrational
    5154 
    51555281    def simplify_factorial(self):
    51565282        """
    51575283        Simplify by combining expressions with factorials, and by
     
    52155341        among the components of the expression for simplifications based on
    52165342        factoring and partial fraction expansions of exponents."
    52175343       
    5218         ALIAS: radical_simplify, simplify_radical, simplify_log,
    5219         log_simplify, exp_simplify, simplify_exp are all the same
     5344        ALIAS: radical_simplify, simplify_radical, exp_simplify, simplify_exp
     5345        are all the same
    52205346       
    52215347        EXAMPLES::
    52225348       
     
    52475373        maxima.eval('domain: complex$')
    52485374        return res
    52495375
    5250     radical_simplify = simplify_log = log_simplify = simplify_radical
     5376    radical_simplify = simplify_radical
    52515377    simplify_exp = exp_simplify = simplify_radical
    52525378
     5379    def simplify_log(self,method=None):
     5380        r"""
     5381        Simplifies symbolic expression, which can contain logs.
     5382
     5383        Recursively scans the expression self, transforming
     5384        subexpressions of the form a1*log(b1) + a2*log(b2) + c into
     5385        log(b1^a1 * b2^a2) + c and simplifies inside logarithm. User
     5386        can specify, which conditions must satisfy a1 and a2 to use
     5387        this transformation in optional parameter ``method``.
     5388
     5389        INPUT:
     5390       
     5391        - ``self`` - expression to be simplified
     5392
     5393        - ``method`` - (default: None) optional, governs the condition
     5394          on a1 and a2 which must be satisfied to contract expression
     5395          a1*log(b1) + a2*log(b2). Values are
     5396         
     5397          - None (use Maxima default, integers),
     5398
     5399          - 'one' (1 and -1),
     5400
     5401          - 'ratios' (integers and fractions of integers),
     5402
     5403          - 'constants' (constants),
     5404
     5405          - 'all' (all expressions). 
     5406
     5407          See also examples below.
     5408           
     5409        DETAILS: This uses the Maxima logcontract() command. From the
     5410        Maxima documentation: "Recursively scans the expression expr,
     5411        transforming subexpressions of the form a1*log(b1) +
     5412        a2*log(b2) + c into log(ratsimp(b1^a1 * b2^a2)) + c. The user
     5413        can control which coefficients are contracted by setting the
     5414        option logconcoeffp to the name of a predicate function of one
     5415        argument. E.g. if you like to generate SQRTs, you can do
     5416        logconcoeffp:'logconfun$ logconfun(m):=featurep(m,integer) or
     5417        ratnump(m)$ . Then logcontract(1/2*log(x)); will give
     5418        log(sqrt(x))."
     5419       
     5420        ALIAS: :meth:`log_simplify` and :meth:`log_simplify` are the
     5421        same
     5422
     5423        EXAMPLES::
     5424       
     5425            sage: x,y,t=var('x y t')
     5426       
     5427        Only two first terms are contracted in the following example ,
     5428        the logarithm with coefficient 1/2 is not contracted::
     5429       
     5430            sage: f = log(x)+2*log(y)+1/2*log(t)
     5431            sage: f.simplify_log()
     5432            log(x*y^2) + 1/2*log(t)
     5433           
     5434        To contract all terms in previous example use option ``method``::
     5435       
     5436            sage: f.simplify_log(method='ratios')
     5437            log(sqrt(t)*x*y^2)
     5438
     5439        This shows that the option ``method`` from the previous call
     5440        has no influence to future calls (we changed some default
     5441        Maxima flag and have to ensure, that this flag has been
     5442        restored)::
     5443       
     5444            sage: f.simplify_log('one')
     5445            1/2*log(t) + log(x) + 2*log(y)
     5446
     5447            sage: f.simplify_log('ratios')
     5448            log(sqrt(t)*x*y^2)     
     5449
     5450            sage: f.simplify_log()
     5451            log(x*y^2) + 1/2*log(t)     
     5452
     5453        To contract terms with no coefficient (more preciselly, with
     5454        coefficients 1 and -1) use option ``method``::
     5455       
     5456            sage: f = log(x)+2*log(y)-log(t)   
     5457            sage: f.simplify_log('one')   
     5458            2*log(y) + log(x/t)
     5459
     5460        ::
     5461
     5462            sage: f = log(x)+log(y)-1/3*log((x+1))
     5463            sage: f.simplify_log()
     5464            -1/3*log(x + 1) + log(x*y)
     5465
     5466            sage: f.simplify_log('ratios')
     5467            log(x*y/(x + 1)^(1/3))
     5468
     5469        pi is irrational number, to contract logarithms in the following example
     5470        we have to put ``method`` to ``constants`` or ``all``::
     5471
     5472            sage: f = log(x)+log(y)-pi*log((x+1))
     5473            sage: f.simplify_log('constants')
     5474            log(x*y/(x + 1)^pi)
     5475
     5476        x*log(9) is contracted only if ``method`` is ``all``::
     5477
     5478            sage: (x*log(9)).simplify_log()
     5479            x*log(9)
     5480            sage: (x*log(9)).simplify_log('all')
     5481            log(9^x)
     5482
     5483        TESTS::   
     5484       
     5485        This shows that the issue at trac #7344 is fixed::
     5486
     5487            sage: (log(sqrt(2)-1)+log(sqrt(2)+1)).simplify_full()
     5488            0
     5489           
     5490        Due to a bug SF 2835634 in Maxima, the following logarithms are not contracted::
     5491       
     5492            sage: (log(5)-log(2)).simplify_log()
     5493            -log(2) + log(5)
     5494
     5495        AUTHORS:
     5496       
     5497        - Robert Marik (11-2009)
     5498        """
     5499        from sage.calculus.calculus import maxima
     5500        maxima.eval('domain: real$')
     5501        if method is not None:
     5502            maxima.eval('logconcoeffp:\'logconfun$')
     5503        if method == 'ratios':
     5504            maxima.eval('logconfun(m):= featurep(m,integer) or ratnump(m)$')
     5505        elif method == 'one':
     5506            maxima.eval('logconfun(m):= is(m=1) or is(m=-1)$')
     5507        elif method == 'constants':
     5508            maxima.eval('logconfun(m):= constantp(m)$')
     5509        elif method == 'all':
     5510            maxima.eval('logconfun(m):= true$')
     5511        elif method is not None:
     5512            raise NotImplementedError, "unknown method, see the help for available methods"
     5513        res = self.parent()(self._maxima_().logcontract())
     5514        maxima.eval('domain: complex$')
     5515        if method is not None:
     5516            maxima.eval('logconcoeffp:false$') 
     5517        return res
     5518
     5519    log_simplify = simplify_log
     5520
     5521    def expand_log(self,method='products'):
     5522        r"""
     5523        Simplifies symbolic expression, which can contain logs.
     5524
     5525        Expands logarithms of powers, logarithms of products and
     5526        logarithms of quotients.  the option ``mehotd`` tells, which
     5527        expression should be expanded.
     5528
     5529        INPUT:
     5530       
     5531        - ``self`` - expression to be simplified
     5532
     5533        - ``method`` - (default: 'product') optional, governs which
     5534          expression is expanded. Possible values are
     5535         
     5536          - 'nothing' (no expansion),
     5537
     5538          - 'powers' (log(a^r) is expanded),
     5539
     5540          - 'products' (like 'powers' and also log(a*b) are expanded),
     5541         
     5542          - 'all' (all possible expansion).
     5543
     5544          See also examples below.       
     5545       
     5546        DETAILS: This uses the Maxima simplifier and sets
     5547        ``logexpand`` option for this simplifier. From the Maxima
     5548        documentation: "Logexpand:true causes log(a^b) to become
     5549        b*log(a). If it is set to all, log(a*b) will also simplify to
     5550        log(a)+log(b). If it is set to super, then log(a/b) will also
     5551        simplify to log(a)-log(b) for rational numbers a/b,
     5552        a#1. (log(1/b), for integer b, always simplifies.) If it is
     5553        set to false, all of these simplifications will be turned
     5554        off. "
     5555       
     5556        ALIAS: :meth:`log_expand` and :meth:`expand(log)` are the same
     5557
     5558        EXAMPLES::
     5559
     5560        By default powers and products (and quotients) are expanded,
     5561        but not quotients of integers::
     5562       
     5563            sage: (log(3/4*x^pi)).log_expand()
     5564            pi*log(x) + log(3/4)
     5565
     5566        To expand also log(3/4) use ``method='all'``::
     5567
     5568            sage: (log(3/4*x^pi)).log_expand('all')
     5569            pi*log(x) + log(3) - log(4)
     5570
     5571        To expand only the power use ``method='powers'``.::
     5572
     5573            sage: (log(x^6)).log_expand('powers')
     5574            6*log(x)
     5575
     5576        The expression ``log((3*x)^6)`` is not expanded with
     5577        ``method='powers'``, since it is converted into product
     5578        first::
     5579       
     5580            sage: (log((3*x)^6)).log_expand('powers')
     5581            log(729*x^6)
     5582
     5583               
     5584        AUTHORS:
     5585       
     5586        - Robert Marik (11-2009)
     5587        """
     5588        from sage.calculus.calculus import maxima
     5589        maxima.eval('domain: real$')
     5590        if method == 'nothing':
     5591            maxima_method='false'
     5592        elif method == 'powers':
     5593            maxima_method='true'
     5594        elif method == 'products':
     5595            maxima_method='all'
     5596        elif method == 'all':
     5597            maxima_method='super'
     5598        else:
     5599            raise NotImplementedError, "unknown method, see the help for available methods"
     5600        self_m = self._maxima_()
     5601        res = self_m.ev("logexpand:%s"%maxima_method)
     5602        res = res.sage()
     5603        maxima.eval('domain: complex$')
     5604        return res   
     5605
     5606    log_expand = expand_log
     5607
    52535608
    52545609    def factor(self, dontfactor=[]):
    52555610        """