Ticket #9989: trac_9989-operands.patch

File trac_9989-operands.patch, 11.4 KB (added by burcin, 11 years ago)
  • module_list.py

    # HG changeset patch
    # User Burcin Erocal <burcin@erocal.org>
    # Date 1285279688 -7200
    # Node ID 461ef737bf9b1b960d8198375274da2ea9e1a0ab
    # Parent  8f40b6c26415dbfc905416de3ac65c40a38972d4
    trac 9989: allow easier access to parts of a symbolic expression.
    
    diff --git a/module_list.py b/module_list.py
    a b  
    15601560              depends = [SAGE_ROOT + "/local/include/pynac/ginac.h"],
    15611561              libraries = ["pynac", "gmp"]),
    15621562   
     1563    Extension('sage.symbolic.getitem',
     1564              sources = ['sage/symbolic/getitem.pyx'],
     1565              language = 'c++',
     1566              depends = [SAGE_ROOT + "/local/include/pynac/ginac.h"],
     1567              libraries = ["pynac", "gmp"]),
     1568   
    15631569    Extension('sage.symbolic.function',
    15641570              sources = ['sage/symbolic/function.pyx'],
    15651571              language = 'c++',
  • sage/symbolic/expression.pyx

    diff --git a/sage/symbolic/expression.pyx b/sage/symbolic/expression.pyx
    a b  
    130130import sage.rings.integer
    131131import sage.rings.rational
    132132from sage.structure.element cimport ModuleElement, RingElement, Element
     133from sage.symbolic.getitem cimport OperandsWrapper
    133134from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction
    134135from sage.rings.rational import Rational  # Used for sqrt.
    135136from sage.misc.derivative import multi_derivative
     
    37273728            sage: a[:SR(5)]
    37283729            [0, 1, 2, 3, 4]
    37293730        """
    3730         return int(self._integer_())   
     3731        return int(self._integer_())
    37313732
    37323733    def iterator(self):
    37333734        """
     
    37453746        """
    37463747        return new_ExpIter_from_Expression(self)
    37473748
    3748 ##     def __getitem__(self, ind):
    3749 ##         """
    3750 ##         EXAMPLES::
    3751        
    3752 ##             sage: x,y,z = var('x,y,z')
    3753 ##             sage: e = x + x*y + z^y + 3*y*z; e
    3754 ##             x*y + 3*y*z + z^y + x
    3755 ##             sage: e[1]
    3756 ##             3*y*z
    3757 ##             sage: e[-1]
    3758 ##             x
    3759 ##             sage: e[1:]
    3760 ##             [3*y*z, z^y, x]
    3761 ##             sage: e[:2]
    3762 ##             [x*y, 3*y*z]
    3763 ##             sage: e[-2:]
    3764 ##             [z^y, x]
    3765 ##             sage: e[:-2]
    3766 ##             [x*y, 3*y*z]
    3767 
    3768 ##         """
    3769 ##         cdef int bind, eind, step, i
    3770 ##         cdef int n_ops = self._gobj.nops()
    3771 ##         if PY_TYPE_CHECK(ind, slice):
    3772 ##             if ind.start:
    3773 ##                 bind = ind.start
    3774 ##                 if bind != ind.start:
    3775 ##                     raise ValueError, "integer index expected"
    3776 ##                 if bind < 0:
    3777 ##                     bind = n_ops + bind
    3778 ##             else:
    3779 ##                 bind = 0
    3780 ##             if ind.stop:
    3781 ##                 eind = ind.stop
    3782 ##                 if eind != ind.stop:
    3783 ##                     raise ValueError, "integer index expected"
    3784 ##                 if eind > n_ops:
    3785 ##                     eind = n_ops
    3786 ##                 if eind < 0:
    3787 ##                     eind = n_ops + eind
    3788 ##             else:
    3789 ##                 eind = n_ops
    3790 ##             if ind.step:
    3791 ##                 step = ind.step
    3792 ##                 if step != ind.step:
    3793 ##                     raise ValueError, "step value must be an integer"
    3794 ##             else:
    3795 ##                 step = 1
    3796 ##             return [new_Expression_from_GEx(self._parent, self._gobj.op(i))
    3797 ##                     for i in xrange(bind, eind, step)]
    3798 
    3799 ##         try:
    3800 ##             bind = ind
    3801 ##             if bind != ind:
    3802 ##                 raise ValueError, "integer index expected"
    3803 ##         except TypeError:
    3804 ##             raise TypeError, "index should either be a slice object, or an integer"
    3805 ##         if bind < 0:
    3806 ##             bind = n_ops + bind
    3807 ##         return new_Expression_from_GEx(self._parent, self._gobj.op(bind))
     3749    property op:
     3750        def __get__(self):
     3751            """
     3752            TESTS::
     3753
     3754                sage: t = 1+x+x^2
     3755                sage: t.op
     3756                Operands wrapper for expression x^2 + x + 1
     3757                sage: x.op
     3758                Traceback (most recent call last):
     3759                ...
     3760                TypeError: cannot index numeric, constant or symbol
     3761                sage: t.op[0]
     3762                x^2
     3763            """
     3764            if is_a_symbol(self._gobj) or is_a_constant(self._gobj) or \
     3765                is_a_numeric(self._gobj):
     3766                    raise TypeError, "cannot index numeric, constant or symbol"
     3767            cdef OperandsWrapper res = OperandsWrapper.__new__(OperandsWrapper)
     3768            res._expr = self
     3769            return res
    38083770
    38093771    def n(self, prec=None, digits=None):
    38103772        """
  • 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) 2010 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: cannot index numeric, constant or symbol
     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, "cannot index numeric, constant or symbol"
     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