Is there a specific reason why you want to use evalf() to do your lookups, or 
is it just because you want something that will recurse down the expression 
tree?  

Aaron Meurer

On Jun 2, 2011, at 2:10 PM, luke wrote:

> 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 <luca.giaco...@gmail.com> 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 <luca.giaco...@gmail.com> 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 <luca.giaco...@gmail.com> 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 <ronan.l...@gmail.com> 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 <ronan.l...@gmail.com> 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 <matt...@gmail.com> wrote:
>>>>>>>>> Hi,
>> 
>>>>>>>>> On 2 June 2011 09:07, luke <luca.giaco...@gmail.com> 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 sympy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sympy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sympy?hl=en.
> 

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

Reply via email to