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.