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 unsubscribe from this group, send email to
> > > > > [email protected].
> > > > > For more options, visit this group at
> > > > >http://groups.google.com/group/sympy?hl=en.
> >
> > > > Mateusz
>
--
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.