#9556 closed enhancement (fixed)
Dynamic attributes for symbolic expressions
Reported by: | Simon King | Owned by: | |
---|---|---|---|
Priority: | major | Milestone: | sage-5.11 |
Component: | symbolics | Keywords: | symbolic expression dynamic attribute sd48 |
Cc: | Burcin Erocal, Karl-Dieter Crisman, Volker Braun, Eviatar Bach | 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 12 years ago by
Attachment: | trac-9556_dynamic_attributes_symbolics.patch added |
---|
comment:1 Changed 12 years ago by
Owner: | segfaulting doctests deleted |
---|
comment:2 Changed 12 years ago by
Cc: | Burcin Erocal added |
---|---|
Status: | new → needs_work |
Work issues: | → 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 12 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 12 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 12 years ago by
Attachment: | trac_9556-dynamic_class.patch added |
---|
comment:5 Changed 12 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:7 Changed 12 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 Changed 12 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 11 years ago by
Cc: | Karl-Dieter Crisman added |
---|
comment:10 Changed 9 years ago by
Authors: | → Burcin Erocal, Simon King, Mike Hansen |
---|---|
Cc: | Volker Braun added |
Description: | modified (diff) |
Status: | needs_work → 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 9 years ago by
Cc: | Eviatar Bach added |
---|---|
Keywords: | sd48 added |
comment:13 Changed 9 years ago by
Reviewers: | → Volker Braun |
---|---|
Status: | needs_review → positive_review |
Work issues: | segfaulting doctests |
Looks good to me
Changed 9 years ago by
Attachment: | trac_9556-dynamic_class_everywhere.patch added |
---|
comment:14 Changed 9 years ago by
Merged in: | → sage-5.11.beta3 |
---|---|
Resolution: | → fixed |
Status: | positive_review → closed |
Changed 9 years ago by
Attachment: | trac_9556-dynamic_class_everywhere.part2.patch added |
---|
comment:15 Changed 9 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]
The patch implements dynamic attributes, but some doctests segfault