My bad, sorry for my inexperience with sympy, final code: (this is
really working!)

class SUM(Function):
    nargs = 1
    def _eval_evalf(cls, prec):
        print cls.args
        map = Code("""function () {
                        emit("sum",{%(field)s:this.%(field)s});
                   }""" % {'field':cls.args[0]})
        reduce = Code("""
                    function(key, values) {
                      var sum = 0;
                      values.forEach(function(doc) {
                        if (doc.%(field)s != undefined){
                            sum += doc.%(field)s;
                        }
                      });
                      return {%(field)s:sum};
                    };""" % {'field':cls.args[0]})
        result = db.people.map_reduce(map, reduce, "myresults")
        return result.find_one()['value'][unicode(cls.args[0])]

On Jun 2, 9:36 pm, luke <[email protected]> wrote:
> Nope in spite of my enthusiasm cls.args wont work properly as it will
> give me a <property> and not a <symbol> :) I'll put back my eval since
> it worked. Hope that was of any help!
>
> On Jun 2, 9:32 pm, luke <[email protected]> wrote:
>
>
>
>
>
>
>
> > WAIT, I didn't see that you use
> > self.arg[0]!! that's why my code was giving me 57, I just need to call
> > cls.arg[0], great now I can remove the eval ;)! thanks again
>
> > On Jun 2, 9:30 pm, luke <[email protected]> wrote:
>
> > > Actually you're wrong. Every instance of a class in python has its own
> > > attributes and editing the attributes of one instance of a class wont
> > > modify the attributes of the other classes.
> > > Try:
>
> > > class MyClass:
> > >     def __init__(self,var):
> > >          self.var=var
>
> > > then execute:
>
> > > >>> a = MyClass(10)
> > > >>> print a.var
> > > >>> 10
> > > >>> b = MyClass(20)
> > > >>> print b.var
> > > >>> 20
> > > >>> print a.var, b.var
> > > >>> 10 20
>
> > > This said, my code might effectively not be the best one but my issue
> > > is that in _eval_evalf(cls,arg) the value of arg is not a symbol but
> > > is a float (57 in my case, probably is an index of some internal
> > > variable.. I can't find any documentation about that). So it's pretty
> > > unusable, that's why I set cls.arg. However the code is working so
> > > thanks everybody.
>
> > > On Jun 2, 7:01 pm, Ronan Lamy <[email protected]> wrote:
>
> > > > Le jeudi 02 juin 2011 à 09:35 -0700, luke a écrit :
>
> > > > > Yes, that worked. But I had to restructure the code
>
> > > > > class SUM(Function):
> > > > >     nargs = 2
> > > > >     @classmethod
> > > > >     def eval(cls, arg):
> > > > >         cls.arg=arg
> > > > >     @classmethod
> > > > >     def _eval_evalf(cls, arg):
> > > > >         map = Code("""function () {
> > > > >                         emit("sum",{%(field)s:this.%(field)s});
> > > > >                    }""" % {'field':cls.arg})
> > > > >         reduce = Code("""
> > > > >                     function(key, values) {
> > > > >                       var sum = 0;
> > > > >                       values.forEach(function(doc) {
> > > > >                         if (doc.%(field)s != undefined){
> > > > >                             sum += doc.%(field)s;
> > > > >                         }
> > > > >                       });
> > > > >                       return {%(field)s:sum};
> > > > >                     };""" % {'field':cls.arg})
> > > > >         result = db.people.map_reduce(map, reduce, "myresults")
> > > > >         return result.find_one()['value'][unicode(cls.arg)]
> > > > > Because arg in _eval_evalf was strangely enough a float (more
> > > > > precisely 57, have no clue why :) ).
> > > > > Thank you all guys!
>
> > > > Your implementation won't work correctly: _eval_evalf needs to be an
> > > > ordinary method of the instance in order to access to the argument of
> > > > the function. Note that SUM is a class and that SUM('some_field') is an
> > > > instance of it, so each time you set SUM.arg, all SUM calls will see it
> > > > and since it's reset every time you call SUM(...), you'll get this:
>
> > > > >>> a = SUM('some_field'); SUM('other_field')
> > > > >>> a.evalf()
>
> > > > -> result for SUM('other_field')
>
> > > > So you do need to follow my advice exactly - and remove eval().
>
> > > > > On Jun 2, 6:07 pm, Ronan Lamy <[email protected]> wrote:
> > > > > > Le jeudi 02 juin 2011 à 08:03 -0700, luke a écrit :
>
> > > > > > > Oh, thanks a lot. Now it's working fine.
> > > > > > > Now the only problem is that the code is evaluated when calling
> > > > > > > sympify, and not when calling evalf.
> > > > > > > Could I ask you what is the way to keep the function lazy? I 
> > > > > > > explain
> > > > > > > myself better. Since the function SUM has to operate on a 
> > > > > > > database if
> > > > > > > I have something like that:
>
> > > > > > > >>> SUM('field') + SUM('field') + SUM('field') #not lazy, 
> > > > > > > >>> computes immediately three queries
> > > > > > > >>> 1234
>
> > > > > > > I'm doing the same operation three times and this is not very 
> > > > > > > good in
> > > > > > > terms of performances, what I expected would have been:
>
> > > > > > > >>> a = SUM('field') + SUM('field') + SUM('field') -> 
> > > > > > > >>> 3SUM('field') # one single query
> > > > > > > >>> print a
> > > > > > > >>> 3*SUM('field')
> > > > > > > >>> print N(a)
> > > > > > > >>> 1234
>
> > > > > > > just like other functions work. e.g.
>
> > > > > > > >>> log(10)+log(10)+log(10)
> > > > > > > >>> 3*log(10)
>
> > > > > > Note that your use case isn't handled very well by sympy (for now).
> > > > > > Subclasses of Function are supposed to be symbolic representations 
> > > > > > of
> > > > > > numeric functions that return numeric results for numeric arguments.
>
> > > > > > But there are ways to hack around these implicit assumptions. To get
> > > > > > lazy evaluation, the easiest is probably to use SUM('field',
> > > > > > evaluate=False), which tells the function to store its argument 
> > > > > > without
> > > > > > executing eval(). Then you can evaluate the expression using 
> > > > > > .doit().
> > > > > > You should get something like:
>
> > > > > > >>> a = SUM('field', evaluate=False) + SUM('field', evaluate=False) 
> > > > > > >>> + SUM('field', evaluate=False)
> > > > > > >>> print a
> > > > > > 3*SUM('field')
> > > > > > >>> print a.doit()
>
> > > > > > 1234
>
> > > > > > Otherwise, you could implement your class so that the DB lookup and
> > > > > > calculations are only done when you call evalf() - that should give 
> > > > > > you
> > > > > > exactly the behaviour you expected above. For that, you'd have to
> > > > > > replace your eval() classmethod with an _eval_evalf() method, e.g.
>
> > > > > > def _eval_evalf(self, prec):
> > > > > >     arg = self.args[0]
> > > > > >     < body of your eval() >
>
> > > > > > > On Jun 2, 9:38 am, Mateusz Paprocki <[email protected]> wrote:
> > > > > > > > Hi,
>
> > > > > > > > On 2 June 2011 09:07, luke <[email protected]> wrote:
>
> > > > > > > > > Hi eveyone,
> > > > > > > > > I'm developing an web application which has to interact with 
> > > > > > > > > "user-
> > > > > > > > > defined formulas" of some financial kpis.
> > > > > > > > > I decided to use sympy to have a more solid math engine.
> > > > > > > > > Basically the input I reiceve is very simple, it might be in 
> > > > > > > > > the worst
> > > > > > > > > case something like:
>
> > > > > > > > > kpi -> "(log(sum('production'))*count('sales')/min('spread')" 
> > > > > > > > > (this
> > > > > > > > > formula is totally made-up)
>
> > > > > > > > > I defined some functions to interact with the database 
> > > > > > > > > according to
> > > > > > > > > the official docs and they seem to be working:
>
> > > > > > > > > For example defining
>
> > > > > > > > > from sympy.core.function import Function
>
> > > > > > > > > class SUM(Function):
> > > > > > > > >    nargs = 2
> > > > > > > > >    @classmethod
> > > > > > > > >    def eval(cls, arg):
> > > > > > > > >        map = Code("""function () {
> > > > > > > > >                        emit("sum",{%(field)s:this.%(field)s});
> > > > > > > > >                   }""" % {'field':arg})
> > > > > > > > >        reduce = Code("""
> > > > > > > > >                    function(key, values) {
> > > > > > > > >                      var sum = 0;
> > > > > > > > >                      values.forEach(function(doc) {
> > > > > > > > >                        if (doc.%(field)s != undefined){
> > > > > > > > >                            sum += doc.%(field)s;
> > > > > > > > >                        }
> > > > > > > > >                      });
> > > > > > > > >                      return {%(field)s:sum};
> > > > > > > > >                    };""" % {'field':arg})
> > > > > > > > >        result = db.people.map_reduce(map, reduce, "myresults")
> > > > > > > > >        return result.find_one()['value'][unicode(arg)]
>
> > > > > > > > > #EOF
>
> > > > > > > > > Then from the command line I can type:
>
> > > > > > > > > >>> print SUM("field")
> > > > > > > > > >>> 1923
>
> > > > > > > > Very interesting application. I'm not sure if you are familiar 
> > > > > > > > with this,
> > > > > > > > but staying on the safe side note here that SUM("field") 
> > > > > > > > doesn't do exactly
> > > > > > > > what you expect, but the outcome is fine. Usually, SymPy's 
> > > > > > > > functions don't
> > > > > > > > accept raw string arguments, but sympify() them:
>
> > > > > > > > In [1]: class fun(Function):
> > > > > > > >    ...:     nargs = 1
> > > > > > > >    ...:     @classmethod
> > > > > > > >    ...:     def eval(cls, arg):
> > > > > > > >    ...:         print type(arg)
> > > > > > > >    ...:
> > > > > > > >    ...:
>
> > > > > > > > In [2]: fun('abc')
> > > > > > > > <class 'sympy.core.symbol.Symbol'>
> > > > > > > > Out[2]: fun(abc)
>
> > > > > > > > So in eval() you got a symbol not string 'abc', but as str() of 
> > > > > > > > a Symbol is
> > > > > > > > simply the name of the symbol, then this (and your code) works 
> > > > > > > > as expected:
>
> > > > > > > > In [3]: print "---%s---" % Symbol('abc')
> > > > > > > > ---abc---
>
> > > > > > > > > But when I try to use sympify my function doesn't evaluate..
>
> > > > > > > > > >>> print sympify("SUM('field')").evalf()
> > > > > > > > > >>> SUM(field)
> > > > > > > > > >>> N("SUM('field')")
> > > > > > > > > >>> SUM(field)
>
> > > > > > > > The problem here is that SymPy, precisely speaking sympify(), 
> > > > > > > > doesn't know
> > > > > > > > what SUM() is, because SUM() resides in the global namespace of 
> > > > > > > > the
> > > > > > > > interpreter, which is unknown to sympify(), e.g.:
>
> > > > > > > > In [4]: sympify("whatever(10)")
> > > > > > > > Out[4]: whatever(10)
>
> > > > > > > > whatever() is unknown to sympify(), so a new Function object is 
> > > > > > > > constructed
> > > > > > > > for it. The same for fun() which I defined above:
>
> > > > > > > > In [6]: sympify("fun(10)")
> > > > > > > > Out[6]: fun(10)
>
> ...
>
> read more »

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sympy?hl=en.

Reply via email to