#9556 closed enhancement (fixed)
Dynamic attributes for symbolic expressions
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: | sage-5.11.beta3 |
Authors: | Burcin Erocal, Simon King, Mike Hansen | Reviewers: | Volker Braun |
Report Upstream: | N/A | Work issues: | |
Branch: | Commit: | ||
Dependencies: | Stopgaps: |
Description (last modified by )
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.'
Attachments (4)
Change History (21)
Changed 7 years ago by
comment:1 Changed 7 years ago by
- Owner changed from segfaulting doctests to (none)
comment:2 Changed 7 years ago by
- 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: ↓ 7 Changed 7 years ago by
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: ↓ 8 Changed 7 years ago by
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 7 years ago by
comment:5 Changed 7 years ago by
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 7 years ago by
Also, all tests pass with this.
comment:7 in reply to: ↑ 3 Changed 7 years ago by
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 7 years ago by
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 6 years ago by
- Cc kcrisman added
comment:10 Changed 4 years ago by
- 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.
comment:11 Changed 4 years ago by
- Cc eviatarbach added
- Keywords sd48 added
comment:12 Changed 4 years ago by
patchbot apply trac_9556-dynamic_class_everywhere.patch
comment:13 Changed 4 years ago by
- Reviewers set to Volker Braun
- Status changed from needs_review to positive_review
- Work issues segfaulting doctests deleted
Looks good to me
Changed 4 years ago by
comment:14 Changed 4 years ago by
- Merged in set to sage-5.11.beta3
- Resolution set to fixed
- Status changed from positive_review to closed
Changed 4 years ago by
comment:15 Changed 4 years ago by
As discussed with Burcin, patch needs to move to a different ticket.
Also, unpacking goes too far and doesn't let me preserve SR objects. This leads to funny behavior for constant functions etc::
sage: o = (SR(1), x) sage: map(type, o) [sage.symbolic.expression.Expression, sage.symbolic.expression.Expression] sage: o = SR._force_pyobject(o)._unpack_operands() sage: map(type, o) [sage.rings.integer.Integer, sage.symbolic.expression.Expression]
comment:16 Changed 4 years ago by
For future historians: The part2 was moved to #14802
comment:17 Changed 16 months ago by
These EvaluationMethods
should be a new-style class: #20825.
The patch implements dynamic attributes, but some doctests segfault