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) > > > > > > > > To overcome this, pass globals() to sympify(), e.g.: > > > > > > > > In [7]: sympify("fun(10)", globals()) > > > > > > > <class 'sympy.core.numbers.Integer'> > > > > > > > Out[7]: fun(10) > > > > > > > > (in your case this will be sympify("SUM('field')", globals())). > > > > > > > > > Am I doing anything wrong? > > > > > > > > Thanks in advance! > > > > > > > > > -- > > > > > > > > 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 > > ... > > 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.
