Ticket #9989: trac_9989-operands.take2.patch

File trac_9989-operands.take2.patch, 11.6 KB (added by burcin, 10 years ago)
  • module_list.py

    # HG changeset patch
    # User Burcin Erocal <burcin@erocal.org>
    # Date 1300881994 -3600
    # Node ID 676959905560154cc743aac49b63281e56eb12e9
    # Parent  5b0fbfaae12fc9f3eb25dd1ce477c47c0f9150d6
    trac 9989: allow easier access to parts of a symbolic expression.
    
    diff --git a/module_list.py b/module_list.py
    a b  
    16501650              depends = ginac_depends,
    16511651              libraries = ["pynac", "gmp"]),
    16521652   
     1653    Extension('sage.symbolic.getitem',
     1654              sources = ['sage/symbolic/getitem.pyx'],
     1655              language = 'c++',
     1656              depends = [SAGE_ROOT + "/local/include/pynac/ginac.h"],
     1657              libraries = ["pynac", "gmp"]),
     1658   
    16531659    Extension('sage.symbolic.function',
    16541660              sources = ['sage/symbolic/function.pyx'],
    16551661              language = 'c++',
  • sage/symbolic/expression.pyx

    diff --git a/sage/symbolic/expression.pyx b/sage/symbolic/expression.pyx
    a b  
    131131import sage.rings.integer
    132132import sage.rings.rational
    133133from sage.structure.element cimport ModuleElement, RingElement, Element
     134from sage.symbolic.getitem cimport OperandsWrapper
    134135from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction
    135136from sage.rings.rational import Rational  # Used for sqrt.
    136137from sage.misc.derivative import multi_derivative
     
    37553756            sage: a[:SR(5)]
    37563757            [0, 1, 2, 3, 4]
    37573758        """
    3758         return int(self._integer_())   
     3759        return int(self._integer_())
    37593760
    37603761    def iterator(self):
    37613762        """
     
    37733774        """
    37743775        return new_ExpIter_from_Expression(self)
    37753776
    3776 ##     def __getitem__(self, ind):
    3777 ##         """
    3778 ##         EXAMPLES::
    3779        
    3780 ##             sage: x,y,z = var('x,y,z')
    3781 ##             sage: e = x + x*y + z^y + 3*y*z; e
    3782 ##             x*y + 3*y*z + z^y + x
    3783 ##             sage: e[1]
    3784 ##             3*y*z
    3785 ##             sage: e[-1]
    3786 ##             x
    3787 ##             sage: e[1:]
    3788 ##             [3*y*z, z^y, x]
    3789 ##             sage: e[:2]
    3790 ##             [x*y, 3*y*z]
    3791 ##             sage: e[-2:]
    3792 ##             [z^y, x]
    3793 ##             sage: e[:-2]
    3794 ##             [x*y, 3*y*z]
    3795 
    3796 ##         """
    3797 ##         cdef int bind, eind, step, i
    3798 ##         cdef int n_ops = self._gobj.nops()
    3799 ##         if PY_TYPE_CHECK(ind, slice):
    3800 ##             if ind.start:
    3801 ##                 bind = ind.start
    3802 ##                 if bind != ind.start:
    3803 ##                     raise ValueError, "integer index expected"
    3804 ##                 if bind < 0:
    3805 ##                     bind = n_ops + bind
    3806 ##             else:
    3807 ##                 bind = 0
    3808 ##             if ind.stop:
    3809 ##                 eind = ind.stop
    3810 ##                 if eind != ind.stop:
    3811 ##                     raise ValueError, "integer index expected"
    3812 ##                 if eind > n_ops:
    3813 ##                     eind = n_ops
    3814 ##                 if eind < 0:
    3815 ##                     eind = n_ops + eind
    3816 ##             else:
    3817 ##                 eind = n_ops
    3818 ##             if ind.step:
    3819 ##                 step = ind.step
    3820 ##                 if step != ind.step:
    3821 ##                     raise ValueError, "step value must be an integer"
    3822 ##             else:
    3823 ##                 step = 1
    3824 ##             return [new_Expression_from_GEx(self._parent, self._gobj.op(i))
    3825 ##                     for i in xrange(bind, eind, step)]
    3826 
    3827 ##         try:
    3828 ##             bind = ind
    3829 ##             if bind != ind:
    3830 ##                 raise ValueError, "integer index expected"
    3831 ##         except TypeError:
    3832 ##             raise TypeError, "index should either be a slice object, or an integer"
    3833 ##         if bind < 0:
    3834 ##             bind = n_ops + bind
    3835 ##         return new_Expression_from_GEx(self._parent, self._gobj.op(bind))
     3777    property op:
     3778        def __get__(self):
     3779            """
     3780            TESTS::
     3781
     3782                sage: t = 1+x+x^2
     3783                sage: t.op
     3784                Operands wrapper for expression x^2 + x + 1
     3785                sage: x.op
     3786                Traceback (most recent call last):
     3787                ...
     3788                TypeError: expressions containing only a numeric coefficient, constant or symbol have no operands
     3789                sage: t.op[0]
     3790                x^2
     3791            """
     3792            if is_a_symbol(self._gobj) or is_a_constant(self._gobj) or \
     3793                is_a_numeric(self._gobj):
     3794                    raise TypeError, "expressions containing only a numeric coefficient, constant or symbol have no operands"
     3795            cdef OperandsWrapper res = OperandsWrapper.__new__(OperandsWrapper)
     3796            res._expr = self
     3797            return res
    38363798
    38373799    def _numerical_approx(self, prec=None, digits=None):
    38383800        """
  • new file sage/symbolic/getitem.pxd

    diff --git a/sage/symbolic/getitem.pxd b/sage/symbolic/getitem.pxd
    new file mode 100644
    - +  
     1include "../libs/ginac/decl.pxi"
     2from sage.symbolic.expression cimport Expression
     3from sage.structure.sage_object cimport SageObject
     4
     5cdef class OperandsWrapper(SageObject):
     6    cdef Expression _expr
  • new file sage/symbolic/getitem.pyx

    diff --git a/sage/symbolic/getitem.pyx b/sage/symbolic/getitem.pyx
    new file mode 100644
    - +  
     1###############################################################################
     2#   Sage: Open Source Mathematical Software
     3#       Copyright (C) 2011 Burcin Erocal <burcin@erocal.org>
     4#  Distributed under the terms of the GNU General Public License (GPL),
     5#  version 2 or any later version.  The full text of the GPL is available at:
     6#                  http://www.gnu.org/licenses/
     7###############################################################################
     8from sage.symbolic.expression cimport new_Expression_from_GEx
     9
     10cdef inline int normalize_index(object arg, int nops, object err_msg) except -1:
     11    """
     12    Given an index ``arg`` and the number of operands ``nops`` return
     13    an integer between 0 and ``nops`` which will be used as the index to fetch
     14    the data from the underlying vector.
     15
     16    TESTS::
     17
     18        sage: from sage.symbolic.getitem import normalize_index_for_doctests as normalize_index
     19        sage: normalize_index(-1, 4)
     20        3
     21        sage: normalize_index(1, 4)
     22        1
     23        sage: normalize_index(1.5, 4)
     24        Traceback (most recent call last):
     25        ...
     26        TypeError: some error message
     27        sage: normalize_index(-5, 4)
     28        Traceback (most recent call last):
     29        ...
     30        IndexError: operand index out of range, got -5, expect between -4 and 4
     31        sage: normalize_index(5, 4)
     32        Traceback (most recent call last):
     33        ...
     34        IndexError: operand index out of range, got 5, expect between -4 and 4
     35    """
     36    cdef int i
     37    i = arg
     38    if i != arg:
     39        raise TypeError(err_msg)
     40    if i < 0:
     41        i = nops + i
     42    if i >= nops or i < 0:
     43        raise IndexError("operand index out of range, got %s, expect between %s and %s"%(arg,-nops,nops))
     44    return i
     45
     46def normalize_index_for_doctests(arg, nops):
     47    """
     48    Wrapper function to test ``normalize_index``.
     49
     50    TESTS::
     51
     52        sage: from sage.symbolic.getitem import normalize_index_for_doctests
     53        sage: normalize_index_for_doctests(-1, 4)
     54        3
     55    """
     56    return normalize_index(arg, nops, "some error message")
     57
     58cdef class OperandsWrapper(SageObject):
     59    """
     60    Operands wrapper for symbolic expressions.
     61
     62    EXAMPLES::
     63 
     64        sage: x,y,z = var('x,y,z')
     65        sage: e = x + x*y + z^y + 3*y*z; e
     66        x*y + 3*y*z + z^y + x
     67        sage: e.op[1]
     68        3*y*z
     69        sage: e.op[1,1]
     70        z
     71        sage: e.op[-1]
     72        x
     73        sage: e.op[1:]
     74        [3*y*z, z^y, x]
     75        sage: e.op[:2]
     76        [x*y, 3*y*z]
     77        sage: e.op[-2:]
     78        [z^y, x]
     79        sage: e.op[:-2]
     80        [x*y, 3*y*z]
     81        sage: e.op[-5]
     82        Traceback (most recent call last):
     83        ...
     84        IndexError: operand index out of range, got -5, expect between -4 and 4
     85        sage: e.op[5]
     86        Traceback (most recent call last):
     87        ...
     88        IndexError: operand index out of range, got 5, expect between -4 and 4
     89        sage: e.op[1,1,0]
     90        Traceback (most recent call last):
     91        ...
     92        TypeError: expressions containing only a numeric coefficient, constant or symbol have no operands
     93        sage: e.op[:1.5]
     94        Traceback (most recent call last):
     95        ...
     96        TypeError: slice indices must be integers or None or have an __index__ method
     97        sage: e.op[:2:1.5]
     98        Traceback (most recent call last):
     99        ...
     100        ValueError: step value must be an integer
     101    """
     102    def __getitem__(self, arg):
     103        """
     104        TESTS::
     105
     106           sage: t = 1+x+x^2
     107           sage: t.op[1:]
     108           [x, 1]
     109        """
     110        cdef int ind, nops
     111        cdef int bind, eind, step
     112        cdef GEx cur_ex
     113        if isinstance(arg, slice):
     114            nops = self._expr._gobj.nops()
     115           
     116            slice_err_msg = "slice indices must be integers or None or have an __index__ method"
     117            if arg.start:
     118                bind = normalize_index(arg.start, nops, slice_err_msg)
     119            else:
     120                bind = 0
     121            if arg.stop:
     122                eind = normalize_index(arg.stop, nops, slice_err_msg)
     123            else:
     124                eind = nops
     125            if arg.step:
     126                step = arg.step
     127                if step != arg.step:
     128                    raise ValueError, "step value must be an integer"
     129            else:
     130                step = 1
     131            return [new_Expression_from_GEx(self._expr._parent,
     132                self._expr._gobj.op(ind)) for ind in xrange(bind, eind, step)]
     133
     134
     135        ind_err_msg = "index should either be a slice object, an integer or a list of integers"
     136        if isinstance(arg, (list, tuple)):
     137            # handle nested index
     138            if len(arg) == 0: # or not all(lambda x: x in ZZ for t in args):
     139                raise TypeError, "index should either be a slice object, an integer or a list of integers"
     140            GEx_construct_ex(&cur_ex, self._expr._gobj)
     141            for x in arg:
     142                nops = cur_ex.nops()
     143                if nops == 0:
     144                    raise TypeError, "expressions containing only a numeric coefficient, constant or symbol have no operands"
     145                ind = normalize_index(x, nops, ind_err_msg)
     146                cur_ex = cur_ex.op(ind)
     147            return new_Expression_from_GEx(self._expr._parent, cur_ex)
     148
     149        ind = normalize_index(arg, self._expr._gobj.nops(), ind_err_msg)
     150        return new_Expression_from_GEx(self._expr._parent,
     151                self._expr._gobj.op(ind))
     152
     153    def _repr_(self):
     154        """
     155        TESTS::
     156
     157            sage: (x^2).op
     158            Operands wrapper for expression x^2
     159        """
     160        return "Operands wrapper for expression %s"%(self._expr)
     161
     162    def _latex_(self):
     163        r"""
     164        TESTS::
     165
     166            sage: latex((x^2).op)
     167            \text{Operands wrapper for expression }x^{2}
     168        """
     169        return r"\text{Operands wrapper for expression }%s"%(self._expr._latex_())
     170
     171    def __reduce__(self):
     172        """
     173        TESTS::
     174
     175            sage: (x^2).op.__reduce__()
     176            (<built-in function restore_op_wrapper>, (x^2,))
     177            sage: loads(dumps((x^2).op))
     178            Operands wrapper for expression x^2
     179        """
     180        return restore_op_wrapper, (self._expr,)
     181
     182def restore_op_wrapper(expr):
     183    """
     184    TESTS::
     185
     186        sage: from sage.symbolic.getitem import restore_op_wrapper
     187        sage: restore_op_wrapper(x^2)
     188        Operands wrapper for expression x^2
     189    """
     190    return expr.op