Reputation: 15
I'm trying to write a simple script to do some math, and print out some specific intermediate steps in LaTeX. I've found there's no real simple way to do this using sympy, so I've started manually writing functions to print out each step I want to see.
I need to take a sympy function and format it so that every variable is replaced by it's associated value, I've been accessing the values through a dictionary.
basically,
import sympy as sym
x,a = sym.symbols("x a")
var_Values = {'x':3, 'a':2}
f=(x+1)/a
print(some_Function(f, var_Values))
so that the print statement reads \frac{3+1}{2}
.
I've already tried two methods, using f.subs()
to replace the variable with the value, which prints out 2 in this case, since it evaluates the expression's value.
I've also tried this textual method:
def some_Function(f, var_Values):
expression = sym.latex(f)
for variable in f.free_symbols:
expression = expression.replace(variable.name, var_Values.get(variable.name)
return expression
which is even worse, as it returns \fr2c{3+1}{2}
, which turns more than what I wanted into numbers. It could be possible to get around this by using variable names that don't appear in the LaTeX commands I"m using, but that approach is impossible for me, as I don't choose my variable names.
Upvotes: 0
Views: 1675
Reputation: 19115
What about
>>> with evaluate(False):
... u = f.subs(val_Values)
That gives the (1 + 3)/2
unevaluated result. If you want the order you wrote terms to be respected then you have to do 2 things: create the expression and do the replacement in an unevaluated context and 2) use a printer that doesn't re-order the args:
>>> with evaluate(False):
... a=(1 + x).subs(var_Values)
... b=(x + 1).subs(var_Values)
>>> a,b
(1 + 3, 1 + 3)
>>> p=StrPrinter(dict(order='none'))
>>> p.doprint(a),p.doprint(b)
(1 + 3, 3 + 1)
Upvotes: 0
Reputation: 1370
SymPy is not great when it comes to leaving an expression unchanged because it inherently tries to simplify anything to make any future computations faster. subs
and replace
try to simplify the expression afterwards.
Here is the best I can think of:
import sympy as sym
x, a = sym.symbols("x a")
var_values = {x: 3, a: 2}
f = (x + 1) / a
def some_function(func: sym.Expr, var_dict: dict) -> sym.Expr:
new_dict = {key: sym.Symbol(str(val)) for key, val in var_dict.items()}
result = func.subs(new_dict)
return result
print(some_function(f, var_values))
This produces (3 + 1)/2
. It should work for most cases. Sadly, this does not work in general as SymPy since with addition, it will sort the terms on its own. That means (x + y + 1) / a; var_values = {x: 3, y: 1, a: 2}
produces (1 + 3 + 1)/2
which is not right. I have opened up an issue about this.
The reason why the second method does not produce valid latex is because expression
is a string and has nothing to do with SymPy variables. Running in debug mode or in interactive mode, you'll see that it is "x + \frac{1}{a}"
. When you replace "a"
for 2
, "\frac"
becomes "\fr2c"
. It is best to keep in mind what variable type each object is and to use SymPy Symbols instead of strings when replacing variables in an expression.
Upvotes: 0