Opened 9 years ago

Last modified 4 years ago

#9556 closed enhancement

Dynamic attributes for symbolic expressions — at Version 10

Reported by: SimonKing Owned by:
Priority: major Milestone: sage-5.11
Component: symbolics Keywords: symbolic expression dynamic attribute sd48
Cc: burcin, kcrisman, vbraun, eviatarbach Merged in:
Authors: Burcin Erocal, Simon King, Mike Hansen Reviewers:
Report Upstream: N/A Work issues: segfaulting doctests
Branch: Commit:
Dependencies: Stopgaps:

Description (last modified by burcin)

Let e be a symbolic expression. It may happen that e.operator() has a certain callable attribute, say, foo, that is not a method of Function. In this situation, one would like to use e.foo(), which is supposed to return e.operator().foo(*e.operands()) - apparently this is useful for working with hypergeometric functions (#2516).

Example

       sage: from sage.symbolic.function import BuiltinFunction
        sage: class TFunc(BuiltinFunction):
        ....:     def __init__(self):
        ....:         BuiltinFunction.__init__(self, 'tfunc', nargs=1)
        ....:
        ....:     class EvaluationMethods:
        ....:         def argp1(self, x):
        ....:             '''
        ....:             Some documentation about a bogus function.
        ....:             '''
        ....:             return x+1
        ....:
        ....:         @property
        ....:         def foo(self):
        ....:             return 5
        ....:
        sage: tfunc = TFunc()
        sage: e = tfunc(x); e
        tfunc(x)
        sage: type(e)
        <class '__main__.Expression_with_dynamic_methods'>
        sage: e.argp1()
        x + 1
        sage: e.foo
        5
        sage: x.argp1()
        Traceback (most recent call last):
        ...
        AttributeError: 'sage.symbolic.expression.Expression' object has no
        attribute 'argp1'
        sage: t = (e + 1).op[0]; t
        tfunc(x)
        sage: t
        tfunc(x)
        sage: type(t)
        <class '__main__.Expression_with_dynamic_methods'>
        sage: t.argp1()
        x + 1
        sage: import sagenb.misc.support as s
        sage: s.completions('t.argp', globals(), system='python')
        ['t.argp1']
        sage: t.argp1.__doc__.strip()
        'Some documentation about a bogus function.'

Apply: trac_9556-dynamic_class_everywhere.patch

Change History (12)

Changed 9 years ago by SimonKing

The patch implements dynamic attributes, but some doctests segfault

comment:1 Changed 9 years ago by SimonKing

  • Owner changed from segfaulting doctests to (none)

comment:2 Changed 9 years ago by SimonKing

  • Cc burcin added
  • Status changed from new to needs_work
  • Work issues set to segfaulting doctests

Sorry, accidentally I inserted the work issue "segfaulting doctests" into the owner field.

It would be nice to have a segfaulting example that works interactively.

comment:3 follow-up: Changed 9 years ago by mhansen

What is the typical use case for this? Are you just looking for a place to put the functions for evaluated function? Or do you really want the function to appear on both the function and its evaluations? It seems like the first case is what you really want.

We currently have all of the machinery to do this and it's used for the category code. Using this, you would have something like the following:

class ExampleBuiltin(BuiltinFunction):
    def __init__(self):
        BuiltinFunction.__init__(self, 'ex_func', nargs=0) #arbitrary no of args
 
    class EvaluationMethods:
        def some_function_name(self):
            return len(self.operands())

comment:4 follow-up: Changed 9 years ago by mhansen

I did a little mockup using this idea.

sage: from sage.symbolic.function import BuiltinFunction
sage: class ExampleBuiltin(BuiltinFunction):
....:         def __init__(self):
....:             BuiltinFunction.__init__(self, 'ex_func', nargs=0) #arbitrary no of args
....:     class EvaluationMethods:
....:             def some_function_name(self):
....:                 return len(self.operands())
....: 
sage: ex_func = ExampleBuiltin()
sage: f = ex_func(x, x+1, x+2)
sage: f.some_function_name()
3
sage: abs(f)
abs(ex_func(x, x + 1, x + 2))

Changed 9 years ago by mhansen

comment:5 Changed 9 years ago by mhansen

Note that pickling also works:

sage: loads(dumps(ceil(x))).foo_bar()
4

One issue is that in order to get the dynamic features, you have to go through the call method of the function. Thus, you'd have to change Expression.abs

        return new_Expression_from_GEx(self._parent, g_abs(self._gobj))

to return the dynamic class version. Similarly, unpickling old objects will just return an Expression object.

comment:6 Changed 9 years ago by mhansen

Also, all tests pass with this.

comment:7 in reply to: ↑ 3 Changed 9 years ago by SimonKing

Replying to mhansen:

What is the typical use case for this? Are you just looking for a place to put the functions for evaluated function? Or do you really want the function to appear on both the function and its evaluations?

Sorry that I was absent for some hours.

Unfortunately I don't know what the real use case is. It was a suggestion of Burcin, and I merely tried to implement what he suggested.

Burcin, could you comment on what is really expected?

comment:8 in reply to: ↑ 4 Changed 9 years ago by SimonKing

Replying to mhansen:

I did a little mockup using this idea.

sage: f = ex_func(x, x+1, x+2) sage: f.some_function_name() 3 sage: abs(f) abs(ex_func(x, x + 1, x + 2))

... which seems like the correct answer to me. So, the fact that ex_func has an _abs_ method helps to get the right answer for f.

comment:9 Changed 9 years ago by kcrisman

  • Cc kcrisman added

comment:10 Changed 6 years ago by burcin

  • Authors set to Burcin Erocal, Simon King, Mike Hansen
  • Cc vbraun added
  • Description modified (diff)
  • Status changed from needs_work to needs_review

I uploaded a new patch that uses Mike's dynamic class idea, but applies to all code paths that might generate symbolic expressions with minimal speed penalty. Please review.

Patchbot, apply only trac_9556-dynamic_class_everywhere.patch.

Note: See TracTickets for help on using tickets.