Opened 6 years ago
Last modified 6 years ago
#17553 new defect
substitute_function() can leave limits unevaluated
Reported by: | wonder | Owned by: | |
---|---|---|---|
Priority: | major | Milestone: | sage-6.4 |
Component: | symbolics | Keywords: | |
Cc: | Merged in: | ||
Authors: | Reviewers: | ||
Report Upstream: | N/A | Work issues: | |
Branch: | Commit: | ||
Dependencies: | Stopgaps: |
Description
Here is an example:
┌────────────────────────────────────────────────────────────────────┐ │ Sage Version 6.4.1, Release Date: 2014-11-23 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ sage: l = limit( function('f')(x), x=1 ) sage: l limit(f(x), x, 1) sage: ls = l.substitute_function( function('f'), (1+x).function(x) ) sage: ls limit(x + 1, x, 1) sage: simplify(ls) limit(x + 1, x, 1) sage: maxima(repr(ls)) 2
This is a simplified case of a situation that's been biting me in an expression that's passed to odeint() after being computed: it breaks the integration because it fails to evaluate to a float expression.
Change History (6)
comment:1 Changed 6 years ago by
comment:2 Changed 6 years ago by
It seems that the inert form of "limit" isn't easily transformed back into one that is considered for evaluation, so once the limit is sitting there as an unevaluated one, substituting into it doesn't give you a limit that is attempted to be evaluated. Perhaps we need a "simplify_limit" that substitutes all the "%LIMIT" symbols for "$LIMIT" in a maxima expression:
sage: l = limit( function('f')(x), x=1 ) sage: ls = l.substitute_function( function('f'), (1+x).function(x) ) sage: from sage.libs.ecl import EclObject sage: from sage.interfaces.maxima_lib import max_limit sage: sage: A=maxima_calculus(ls).ecl() sage: B=EclObject([max_limit]).cons(A.cdr()) #the SIMP doesn't matter sage: A <ECL: ((%LIMIT SIMP) ((MPLUS SIMP) 1 |$_SAGE_VAR_x|) |$_SAGE_VAR_x| 1)> sage: B <ECL: (($LIMIT) ((MPLUS SIMP) 1 |$_SAGE_VAR_x|) |$_SAGE_VAR_x| 1)> sage: maxima_calculus(B) 2
Alternatively, perhaps we should change the translation of the "limit" operator to be "$LIMIT" instead of "%LIMIT" (at the cost of re-evaluating any limits when they pass from sage to maxima). I don't know where exactly that conversion happens, because it does seem to be partially in place already:
sage: maxima_calculus(ls).ecl() <ECL: ((%LIMIT SIMP) ((MPLUS SIMP) 1 |$_SAGE_VAR_x|) |$_SAGE_VAR_x| 1)> sage: maxima_calculus(ls.operator()).ecl() <ECL: $LIMIT>
I suspect that this is the culprit:
sage: L=ls.operator() sage: L._maxima_lib_init_() "'limit"
(note the explicit "'" there: it's the inert limit). We also have
See sage.symbolic.function_factory.function_factory
which defines the class NewSymbolicFunction
(is that really a good idea to have that class closed over?) with the relevant _maxima_init_
.
sage: sage.interfaces.maxima_lib.sage_op_dict[L] <ECL: %LIMIT>
which gets picked up because the first encounter of the limit symbol is the inert "%limit" on the maxima side, which primes the dict with that translation. This is easily overridden if desired. However, "simplify" and friends don't use sr_to_max
yet, so it wouldn't make much of a difference here.
In short, we may need to instantiate our own "limit" operator in ginac to accommodate the different translation options, unless we're happy with a "simplify_limit" routine instead.
comment:3 Changed 6 years ago by
Yes, thank you. I was just converging on the same conclusions about 'limit
and using a python function to force re-evaluation.
I've been assuming that when I see "limit( ..., x, 0.1 )" in a result, it's going to be passed back to maxima in a form that'll re-evaluate the limit. I'd vote for implementing it that way rather than requiring the user to do "simplify_limit" manually at the appropriate times. But I'm sure other users are doing infinite sums and such things that are better not to re-evaluate...
comment:4 follow-up: ↓ 5 Changed 6 years ago by
Hmm, I didn't realize you could use python functions in substitute_function
. Just noticed that when looking through expression_conversions.py
. Given that, this seems to work:
sage: l = limit( function('f')(x), x=0.1 ) sage: ls = l.substitute_function( function('f'), (1 - exp(-x)).function(x) ) sage: ls limit(-e^(-x) + 1, x, 0.1) sage: def eval_limit( ex, var, val ): kv = { str(var):val } return limit( ex, **kv ) ....: sage: ls.substitute_function( l.operator(), eval_limit ) 0.09516258196404048
:-)
Note using sage.symbolic.function_factory.function('limit')
in place of l.operator()
does not work.
Update: here it is in simplify_limits
form:
limop = limit( SR('f(x)'), x=0 ).operator() def simplify_limits( expr ): def eval_limit( expr, var, val ): kv = { str(var):val } return limit( expr, **kv ) return expr.substitute_function( limop, eval_limit )
comment:5 in reply to: ↑ 4 ; follow-up: ↓ 6 Changed 6 years ago by
Replying to wonder:
limop = limit( SR('f(x)'), x=0 ).operator() def simplify_limits( expr ): def eval_limit( expr, var, val ): kv = { str(var):val } return limit( expr, **kv ) return expr.substitute_function( limop, eval_limit )
Smart solution. There is an eval_limit
with the right interface already available, though:
limop = limit( SR('f(x)'), x=0 ).operator() def simplify_limits( expr ): return expr.substitute_function( limop, maxima_calculus.sr_limit )
should do the trick. The fact that
sage: limsym=sage.symbolic.function_factory.function('limit') sage: limsym == limop False
indicates that we really should be making a symbol for "limit" beforehand (and equip it with the right translations)
CORRECTION: The symbol to use is sage.calculus.calculus._limit
and the reason for the inequality is an extra attribute for proper latex representation of limits:
sage: limsym=sage.symbolic.function_factory.function('limit',print_latex_func=sage.calculus.calculus._limit_latex_) sage: limsym == limop True
comment:6 in reply to: ↑ 5 Changed 6 years ago by
Replying to nbruin:
return expr.substitute_function( limop, maxima_calculus.sr_limit )
Thanks! Yes, that also works.
Unfortunately that workaround doesn't work when exponentials are involved. The
repr
stage causes the constante
to be replaced by an unbound variablee
:Here's what I'm trying out to work around this:
I've tried poking around the maxima_lib module for a more straightforward way to ask Maxima to evaluate the limit without the data loss involved in the
repr
step, but so far I haven't found my way. Advice would be welcome. Thanks for this wonderfully useful package and for all the bug fixes!