Ask Your Question

Why does simplify break print_latex_func?

asked 2017-04-18 21:17:36 -0500

updated 2017-04-19 08:19:32 -0500

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)))

print latex(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 flag offensive close merge delete


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

Björn gravatar imageBjörn ( 2017-04-19 06:50:49 -0500 )edit

2 answers

Sort by » oldest newest most voted

answered 2017-04-19 08:15:37 -0500

updated 2017-04-19 08:16:10 -0500

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.)

edit flag offensive delete link more



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!

kcrisman gravatar imagekcrisman ( 2017-04-19 09:53:55 -0500 )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.

Björn gravatar imageBjörn ( 2017-04-19 11:46:17 -0500 )edit

answered 2017-04-19 07:02:47 -0500

kcrisman gravatar image

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.

edit flag offensive delete link 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?

Björn gravatar imageBjörn ( 2017-04-19 07:38:07 -0500 )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
ndomes gravatar imagendomes ( 2017-04-19 09:07:36 -0500 )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.

Björn gravatar imageBjörn ( 2017-04-19 09:20:07 -0500 )edit

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
ndomes gravatar imagendomes ( 2017-04-19 10:16:22 -0500 )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.

Björn gravatar imageBjörn ( 2017-04-19 11:52:52 -0500 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower


Asked: 2017-04-18 21:17:36 -0500

Seen: 72 times

Last updated: Apr 19