# Why does simplify break print_latex_func?

Here's a minimal example of what I am talking about. Both print statements should return the same output in this scenario.

,var n x
def my_latex_print(self, *args):
return "a_{%s}" %(', '.join(map(latex, args)))
a=function('a',nargs=1,print_latex_func=my_latex_print)(n)

sexp=a(n=n+1)+a(n=n)
print latex(sexp)
sexp=simplify(expand(sexp))
print latex(sexp)


However, the outputs differ:

a_{n + 1} + a_{n}
a\left(n + 1\right) + a\left(n\right)


Of course simplify-expand may lead to a different expression, but that's not what I am concerned about. I am concerned about the representation of a(n) instead of a_n in the output.

My main question is: How can I recover a_n out of sexp at the end of my code?

edit retag close merge delete

I know it is simplify() and not expand() that causes the problem. But why and how?

( 2017-04-19 13:50:49 +0200 )edit

Sort by » oldest newest most voted

Thanks to kcrisman I figured the remaining bits out myself. Complete code as follows:

# [...]
print latex(sexp)
sexp2=simplify(sexp) # expand doesn't hurt it, so omit that
print latex(sexp2)
# observe they are different due to sexp2 being processed
# via Maxima, as explained by kcrisman
# maxima replaces a() by a new a() that doesn't have the
# print function:

print sexp2.operands()[0].operator().__class__ # maxima a()
print a.__class__ # the a() we have defined

# to coerce back, we need to replace the former by the latter:
print latex(sexp2.substitute_function(sexp2.operands()[0].operator(),a))


(And that does the intended.)

more

1

Very nice work - hope you don't mind I used my mighty powers to make this an answer and accept it for you, since you did the hard part!

( 2017-04-19 16:53:55 +0200 )edit

I guess will have to live with that ;) Thanks again for giving me the starting point, there wasn't much left to do after that.

( 2017-04-19 18:46:17 +0200 )edit

The issue is that simplify and expand send sexp to Maxima, which does not "know" about your custom print function. Then when it comes back to Sage it also doesn't know about it anymore. One way of slightly avoiding this is to call the new thing sexp2, though this doesn't solve the printing problem.

For that, you may have to somehow coerce the expression back to using your a, perhaps using subs.

more

Aha, I see... At the end of the day I don't really mind Maxima, as long as I can coerce back, which I cannot seem to figure out how to achieve. Obvious (to me) candidates would seem to be latex(sexp2.subs({a(n):a(n)})) or even latex(sexp2.subs({a: a})). But they do not work as expected or throw errors when I use keywords in combination with arguments to a().

print sexp2.operands()[0].operator().__class__ # maxima a()
print a.__class__ # my a()


Reveals the functions a() are indeed different, the former function_factory.NewSymbolicFunction, the latter expression.Expression. Can't I just somehow retrospectively set the print_latex_func on the function_factory.NewSymbolicFunction?

( 2017-04-19 14:38:07 +0200 )edit

Very tricky. But IMHO it is annoying that sending an expression through maxima results in two identically named but different functions, see:

print sexp2.operands()[0].operator().name()
print sexp2.operands()[0].operator() is a

( 2017-04-19 16:07:36 +0200 )edit

Not only that, to get the operator name, one somehow needs to find it by location in the sage expression, at least I am not aware of a better way to find a reference to the "maxima" a(). Maybe one could loop over all operators in the expression and somehow filter by a feature that identifies them as maxima operators... but that's messy.

( 2017-04-19 16:20:07 +0200 )edit
1

Looping over all operators:

f = copy(sexp2)
for x in f.operands():
f = f.substitute_function(x.operator(),sage_eval(x.operator().name(),locals=locals()))
sexp2 ; f

( 2017-04-19 17:16:22 +0200 )edit

That's just perfect, thanks! latex(sexp2), latex(f) instead of sexp2 ; f really demonstrates the achievement of your recursive coercion. Very nice, I especially like that the code does not require a priori knowledge of the function to be substituted. This really should be built into sage's simplify() at the stage when it processes what comes back from maxima.

( 2017-04-19 18:52:52 +0200 )edit