Ask Your Question

How to substitute a value into an expression without evaluation to convert it to LaTex?

asked 2023-05-18 18:32:14 +0200

user98448 gravatar image

updated 2023-05-19 18:28:19 +0200

slelievre gravatar image

How can I convert an expression to LaTex while substituting a variable value without evaluating the expression after substitution?

For example, I have an expression 5*x*y. The latex(5*x*y) is 5 \, x y as expected. But now I want to substitute a constant instead of a variable. For example 8 instead of x. The LaTex representation (latex((5*x*y).subs(x=8))) of which is would be 40 \, y, so the expression is automatically evaluated. But I want to get something like 5 \\cdot 8 y instead. Is that possible to do with SageMath?

I know something like that exists in SymPy. For example, take a look at the answer to:

It uses sympy.UnevaluatedExpr to wrap an expression to substitute the variable with. Is there anything like that in SageMath?

edit retag flag offensive close merge delete


Welcome to Ask Sage! Thank you for your question!

slelievre gravatar imageslelievre ( 2023-05-19 21:27:49 +0200 )edit

3 Answers

Sort by ยป oldest newest most voted

answered 2023-05-19 22:17:53 +0200

dazedANDconfused gravatar image

There are several ways to do it and you'll have to decide on which suits you best. First method: Direct use of sympy. This didn't work out the way I expected: it switched x and y after substitution: image description

If that's okay then maybe this is good enough. I wouldn't want it swapping terms, so with a bit of fiddling came up with this, which is getting more involved:

import sympy
from sympy import *
x,y=symbols("x y")
expr = 5*x*y
from sympy import UnevaluatedExpr

Now the order is correct but latex(UnevaluatedExpr(sympify("5*8*y",evaluate=False))) is a bit annoying

image description

Note removing print from the last statement gives you your double escape: '5 \\cdot 8 y'

Second method is to work in strings. This will give you complete control over your output:

expr = 5*x*y
expr = latex(expr).replace(r" \, ","")
s1 = expr.replace("x", r" \\cdot 8 ")
s2 = s1.replace("y",r"\\cdot 4 ")
print(s1,",  ",s2)

The output is:

image description

s1 is the output after the first step of substituting x for r" \cdot 8 " while s2 would be a potential second step of substituting y for r"\cdot 4 ".

Third method, which might be useful depending on the context of how you are using the output, is to work directly in LaTeX and borrow from Sage as needed. This is typically how I work with both. Here is a sample LaTeX document:

Start with the expression $5xy$. Substitute $8$ for $x$ to get
$5 \cdot 8y = \sage{g(y)}$. Now substitute $y=4$ into 
$\sage{g(y)}$ to get $40\cdot 4 = \sage{g(4)}$.

image description

edit flag offensive delete link more

answered 2023-05-20 06:49:56 +0200

user98448 gravatar image

I found another solution, which is better for me. It still requires me to use sympy, which is unfortunate because sympy symbols are incompatible with sage function declarations (TypeError: must construct a function with symbolic variables as arguments), but it's better than nothing.

There is a global thread-local parameters object sympy.core.parameters.global_parameters with a evaluate parameter that controls whether expressions should be evaluated or not. We can explicitly change the value of evaluate parameter:

sympy.core.parameters.global_parameters.evaluate = False

Or do the same, but using context manager class sympy.evaluate that pushes the evaluate parameter value for us:

with sympy.evaluate(False):
    print(x + x)

The evaluate global parameter is restored to the previous value once the code leaves the with statement.

This method is better than using UnevaluatedExpr because it doesn't mess with the order of expressions. For instance:

sympy.latex((5*x*y).subs(x, sympy.UnevaluatedExpr(8)))
'5 y 8'

The order of x (substituted with 8) and y has been changed.

with sympy.evaluate(False):
    print(sympy.latex((5*x*y).subs(x, 8)))

5 \cdot 8 y

The order has not been changed.

I could not find such global parameter in sage. hold argument is False by default (from sage source code):

if kwds.get('hold', False):

While in sympy the default value of hold's alternative evaluate argument is the global parameter evaluate:

eval = options.pop('evaluate', global_parameters.evaluate)

I wish I was wrong and there was actually any way to globally disable expressions evaluation in sagemath. I'm still looking for it :)

edit flag offensive delete link more

answered 2023-05-19 18:40:00 +0200

slelievre gravatar image

Sage has a "hold" context for some operations, but I don't see how to use it to answer the question.

Especially since the hold context seems quite fragile with respect to substitution.

As a workaround, one can use SymPy inside Sage.

Here are some things one can do with "hold", in case it helps.

sage: a = x
sage: a = a.mul(8, hold=True)
sage: a = a.mul(5, hold=True)
sage: a
sage: a.unhold()
edit flag offensive delete link more

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: 2023-05-18 18:32:14 +0200

Seen: 161 times

Last updated: May 20