简体   繁体   English

有没有办法在不自动评估的情况下用 sympy function 中的值替换变量?

[英]Is there a way to replace a variable with a value in a sympy function without automatically evaluating it?

I'm trying to write a simple script to do some math, and print out some specific intermediate steps in LaTeX.我正在尝试编写一个简单的脚本来做一些数学运算,并在 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.我发现使用 sympy 没有真正简单的方法可以做到这一点,所以我开始手动编写函数来打印出我想看到的每个步骤。

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.我需要获取一个 sympy function 并对其进行格式化,以便每个变量都被它的关联值替换,我一直在通过字典访问这些值。

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} .以便打印语句读取\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.我已经尝试了两种方法,使用f.subs()将变量替换为值,在这种情况下打印出 2,因为它计算表达式的值。

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.更糟糕的是,它返回\fr2c{3+1}{2} ,这比我想要的数字要多。 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.可以通过使用未出现在我正在使用的 LaTeX 命令中的变量名来解决此问题,但这种方法对我来说是不可能的,因为我没有选择我的变量名。

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. SymPy 在保持表达式不变时并不是很好,因为它本质上试图简化任何事情以使任何未来的计算更快。 subs and replace try to simplify the expression afterwards. subsreplace尝试在之后简化表达式。

so that the print statement reads \frac{3+1}{2} .以便打印语句读取\frac{3+1}{2}

I think you mean 3 + \frac{1}{2} .我认为您的意思是3 + \frac{1}{2}

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.UnevaluatedExpr(val) for key, val in var_dict.items()}
    x_ = sym.Wild("x")
    new_func = func.replace(x_, sym.UnevaluatedExpr(x_))
    return new_func.subs(new_dict)


print(some_function(f, var_values))

This produces 3 + 2**(-1) .这会产生3 + 2**(-1) In SymPy, 1/x and -x are treated as x**(-1) and -1*x internally.在 SymPy 中, 1/x-x在内部被视为x**(-1)-1*x Hopefully someone with more expertise can help you out with that and give you an answer of 3 + 1/2 instead.希望有更多专业知识的人可以帮助您解决这个问题,并给您3 + 1/2的答案。

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.第二种方法不能产生有效的 latex 的原因是因为expression是一个字符串,与 SymPy 变量无关。 Running in debug mode or in interactive mode, you'll see that it is "x + \frac{1}{a}" .在调试模式或交互模式下运行,您会看到它是"x + \frac{1}{a}" When you replace "a" for 2 , "\frac" becomes "\fr2c" .当您将"a"替换为2时, "\frac"变为"\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.最好记住每个 object 是什么变量类型,并在替换表达式中的变量时使用 SymPy 符号而不是字符串。

I've found something quick and dirty, but it does work:我发现了一些又快又脏的东西,但它确实有效:

def some_Function(f, var_Values):
    expression = sym.latex(f)
    for variable in f.free_symbols:
        expression = expression.replace(variable.name,f"~{var_Values.get(variable.name)}")
        expression = expression.replace(f"~{variable.name}", str(var_Values.get(variable.name)}")
    return expression

It does twice as many replacements as necessary, but it does what I want it to它做了两倍于必要的替换,但它做了我想要的

What about关于什么

>>> with evaluate(False):
...     u = f.subs(val_Values)

That gives the (1 + 3)/2 unevaluated result.这给出了(1 + 3)/2未评估的结果。 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:如果您希望遵守您编写术语的顺序,那么您必须做两件事:创建表达式并在未评估的上下文中进行替换,以及 2) 使用不会重新排序 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM