问题描述
我正在尝试制作一个python proram来查找导数和积分并展示如何操作.到目前为止,我发现有一个integrate_steps函数可以返回所使用的步骤,但是我还没有找到用于区分的等效项.
有人知道是否有同等学历吗?
如果没有,您对如何找到查找导数所需的步骤有任何想法吗?
方法1(手动)
查看代码,
每个节点的值采用以下格式:
function_name参数=>return_value
似乎有些 diff
节点的参数等于返回值,目前我不确定如何解释.
如果该图表以某种方式提及应用了每个规则的位置,它可能会更有用(我想不出一个简单的方法来做到这一点).
这也是此代码:
导入猎人导入系统从猎人进口Q,何时,停止,行动从hunter.actions导入ColorStreamAction公式_ltx = r'''\ documentclass [border = 2pt,varwidth] {字母}\ usepackage {amsmath}\ pagenumbering {gobble}\ begin {document}\ [\ texttt {TITLE} \]\[ 公式 \]\ end {document}'''#==============#==跟踪===#==============从sympy.printing.latex导入LatexPrinter,print_latex,latex全局call_tree_root#一个节点对象,用于保存观察到的函数调用#及其参数,返回值和函数名称类Node(object):def __init __(self,arg = None,retval = None,func_name = None):self.arg = argself.retval = retvalself.arg_ascii ="self.retval_ascii ="self.func_name = func_nameself.uid = 0self.children = []#这是一个猎人动作,我们建立一个调用图并填充它#这样我们以后就可以渲染它##CGBAction(调用图生成器操作)CGBAction(ColorStreamAction)类:def __init __(self,* args,** kwargs):超级(ColorStreamAction,self).__ init __(* args,** kwargs)#自定义调用堆栈self.tstack = []全局call_tree_rootcall_tree_root =节点(arg =",func_name ="root")self.node_idx = 1self.tstack.append(call_tree_root)def __call __(自身,事件):如果event.kind在['return','call']中:如果event.kind =='return':打印(str(event.arg))如果len(self.tstack)>0:顶部= self.tstack.pop()top.retval =乳胶(event.arg)top.retval_ascii = str(event.arg)elif event.kind =='call':打印(str(event.locals.get('self')))新= Node()new.uid = self.node_idxnew.arg = latex(event.locals.get('self'))new.arg_ascii = str(event.locals.get('self'))顶部= self.tstack [-1]self.tstack.append(新)top.children.append(新)new.func_name = event.module +:"+ event.functionself.node_idx + = 1hunter.trace(Q(module_contains ="sympy",function_in = ['_ eval_derivative','diff'],kind_in = ["call","return"],action = CGBAction))从sympy导入*x =符号('x')f = 1/(x * sin(x)** 2)#f = 1/(x * 3)#f = sin(exp(cos(x)* asin(x)))f.diff(x)#============================#==调用图渲染====#============================导入操作系统汇入OUT_DIR =公式".如果不是os.path.exists(OUT_DIR):os.mkdir(OUT_DIR)def write_formula(前缀,uid,公式,标题):TEX = uid +前缀+".tex"PDF = uid +前缀+".pdf"PNG = uid +前缀+".png"TEX_PATH = OUT_DIR +"/";+ TEX将open(TEX_PATH,"w")设为f:ll =公式_ltxll = ll.replace("FORMULA",公式)ll = ll.replace("TITLE",title)f.write(ll)#编译公式CMD =".cd公式;pdflatex {TEX};转换-trim-密度300 {PDF}-质量90-彩色空间RGB {PNG};"格式.(TEX = TEX,PDF = PDF,PNG = PNG)操作系统(CMD)buf_nodes ="buf_edges ="def dfs_tree(x):全局buf_nodes,buf_edgesarg =(如果x.arg为其他x.arg,则为".)rv =(如果x.retval为其他x.retval,则为")arg = arg.replace("\ r",")rv = rv.replace("\ r",")公式= arg +"\\ Rightarrow"+ rvprint(x.func_name +->" +"x.arg_ascii +"->"+ x.retval_ascii)x.func_name = x.func_name.replace("_","\\ _")write_formula(",str(x.uid),formula,x.func_name)buf_nodes + ="{0} [image =" {0} .png"标签="];".".format(x.uid);对于x.children中的y:buf_edges + =" {0}->{1}; \ n".format(x.uid,y.uid);dfs_tree(y)dfs_tree(call_tree_root)g =打开(OUT_DIR +"/graph.dot","w")g.write("digraph g {")g.write(buf_nodes)g.write(buf_edges)g.write(" \ n)g.close()os.system("cd公式;点-Tpng graph.dot> graph.png;"))
将SymPy逻辑映射到区分规则
我认为剩下的步骤是将中间节点从SymPy映射到区分规则.这是我能够绘制的一些图片:
- 产品规则映射到 sympy.core.mul.Mul._eval_derivative
- 链式规则映射到 sympy.core.function.Function._eval_derivative
- 求和规则映射到 sympy.core.add.Add._eval_derivative
- 求和的导数映射到 sympy.concrete.summations.Sum._eval_derivative 通过通用莱布尼兹规则的产品的
- n次衍生产品映射到 sympy.core.mul.Mul._eval_derivative_n_times
- 通用幂规则映射到 sympy.core.power.Pow._eval_derivative
我没有在 sympy.core
中看到分数类,所以商规则通过产品规则和通用幂规则,指数为-1.
运行
要使其运行,您需要:
sudo apt-get install graphviz imagemagick texlive texlive-latex-base
文件/etc/ImageMagick-6/policy.xml
必须用以下行更新,以允许从 PDF-> PNG
转换:
< policy domain =编码器"权利=读|写"pattern ="PDF"/>
还有一个名为 jonga 的调用图库,但这有点通用,并且不允许完全过滤掉不需要的电话.
I'm trying to make a python proram to find derivatives and integrals as well as showing how. I have so far found that there is an integral_steps function which returns the steps used, but I have not found an equivalent for differentiation.
Does anyone know if there is an equivalent?
If there isn't, do you have any ideas on how to find the steps needed to find a derivative?
Method 1 (manual)
Looking at the code, the Derivative class is where the top-level logic lives. That's only the top-level part. From there on, the computation requires computing derivatives of different nodes inside the expression tree.
The logic for each specific node of the expression tree lives in the _eval_derivative
method corresponding to each particular node type.
This would allow you to add code to those _eval_derivative
methods in order to trace the entire process and find all the steps.
Method 2 (using a tracer)
Python has multiple tracing packages. python-hunter written by @ionelmc is quite good actually and fits this use-case well.
Among many other features, it allows installing certain callbacks when a function starts executing, and another one when the function returns its value. In fact that's exactly what we need.
Here's an example that shows how to use this (I ran and tested this on Python 3.7.3, SymPy 1.7 and hunter 3.3.1) :
import hunter
import sys
from hunter import Q, When, Stop
hunter.trace(
Q(module_contains="sympy",function='_eval_derivative',kind_in=["call","return"],action=hunter.CallPrinter(repr_func=str))
)
from sympy import *
x = symbols('x')
f = 1/(x * sin(x)**2)
f.diff(x)
So this allows to pick which data structures we want to inspect, how we want to print them, and it allows us to see the intermediary steps of the differentiation process:
[...]7/site-packages/sympy/core/power.py:1267 call => _eval_derivative(self=sin(x)**(-2), s=x)
[...]7/site-packages/sympy/core/power.py:1267 call => _eval_derivative(self=<sympy.core.power.Pow object at 0x7f5925337150>, s=<sympy.core.symbol.Symbol object at 0x7f5925b6a2b0>)
[...]ite-packages/sympy/core/function.py:598 call => _eval_derivative(self=sin(x), s=x)
[...]ite-packages/sympy/core/function.py:598 call => _eval_derivative(self=<sympy.functions.elementary.trigonometric.sin object at 0x7f592589ee08>, s=<sympy.core.symbol.Symbol object at 0x7f5925b6a2b0>)
[...]ite-packages/sympy/core/function.py:612 return <= _eval_derivative: cos(x)
[...]ite-packages/sympy/core/function.py:612 return <= _eval_derivative: <sympy.functions.elementary.trigonometric.cos object at 0x7f592525fef8>
[...]7/site-packages/sympy/core/power.py:1271 return <= _eval_derivative: -2*cos(x)/sin(x)**3
[...]7/site-packages/sympy/core/power.py:1271 return <= _eval_derivative: <sympy.core.mul.Mul object at 0x7f5925259b48>
[...]7/site-packages/sympy/core/power.py:1267 call => _eval_derivative(self=1/x, s=x)
[...]7/site-packages/sympy/core/power.py:1267 call => _eval_derivative(self=<sympy.core.power.Pow object at 0x7f5925337200>, s=<sympy.core.symbol.Symbol object at 0x7f5925b6a2b0>)
[...]7/site-packages/sympy/core/power.py:1271 return <= _eval_derivative: -1/x**2
[...]7/site-packages/sympy/core/power.py:1271 return <= _eval_derivative: <sympy.core.mul.Mul object at 0x7f5925259f10>
If you want to also cover the diff
function you can alter the code above and have function_in=['_eval_derivative','diff']
. In this way, you can look at not only the partial results, but also the call of the diff
function and its return value.
Method 3 (using a tracer, building a call graph and visualizing it)
Using graphviz, latex and a tracer (again, python-hunter) you can actually see the call graph more clearly. It does take a bit of time to render all the formulas for each intermediary step, because pdflatex
is being used (I'm sure there's faster renderers for latex though).
Each node's value is in the following format:
function_name
argument => return_value
There seem to be a few diff
nodes that have the argument equal to the return value which I'm not sure how to explain at the moment.
The diagram could probably be more useful if it mentioned somehow where each rule was applied (I can't think of an easy way to do that).
Here's the code for this too:
import hunter
import sys
from hunter import Q, When, Stop, Action
from hunter.actions import ColorStreamAction
formula_ltx = r'''
\documentclass[border=2pt,varwidth]{letter}
\usepackage{amsmath}
\pagenumbering{gobble}
\begin{document}
\[ \texttt{TITLE} \]
\[ FORMULA \]
\end{document}
'''
# ==============
# == Tracing ===
# ==============
from sympy.printing.latex import LatexPrinter, print_latex, latex
global call_tree_root
# a node object to hold an observed function call
# with its argument, its return value and its function name
class Node(object):
def __init__(self, arg=None, retval=None, func_name=None):
self.arg = arg
self.retval = retval
self.arg_ascii = ""
self.retval_ascii = ""
self.func_name = func_name
self.uid = 0
self.children = []
# this is a hunter action where we build a call graph and populate it
# so we can later render it
#
# CGBAction (Call Graph Builder Action)
class CGBAction(ColorStreamAction):
def __init__(self, *args, **kwargs):
super(ColorStreamAction, self).__init__(*args, **kwargs)
# a custom call stack
self.tstack = []
global call_tree_root
call_tree_root = Node(arg="",func_name="root")
self.node_idx = 1
self.tstack.append(call_tree_root)
def __call__(self, event):
if event.kind in ['return','call']:
if event.kind == 'return':
print(str(event.arg))
if len(self.tstack) > 0:
top = self.tstack.pop()
top.retval = latex(event.arg)
top.retval_ascii = str(event.arg)
elif event.kind == 'call':
print(str(event.locals.get('self')))
new = Node()
new.uid = self.node_idx
new.arg = latex(event.locals.get('self'))
new.arg_ascii = str(event.locals.get('self'))
top = self.tstack[-1]
self.tstack.append(new)
top.children.append(new)
new.func_name = event.module + ":" + event.function
self.node_idx += 1
hunter.trace(
Q(module_contains="sympy",function_in=['_eval_derivative','diff'],kind_in=["call","return"],action=CGBAction)
)
from sympy import *
x = symbols('x')
f = 1 / (x * sin(x)**2)
#f = 1 / (x * 3)
#f = sin(exp(cos(x)*asin(x)))
f.diff(x)
# ============================
# == Call graph rendering ====
# ============================
import os
import re
OUT_DIR="formulas"
if not os.path.exists(OUT_DIR):
os.mkdir(OUT_DIR)
def write_formula(prefix,uid,formula,title):
TEX = uid + prefix + ".tex"
PDF = uid + prefix + ".pdf"
PNG = uid + prefix + ".png"
TEX_PATH = OUT_DIR + "/" + TEX
with open(TEX_PATH,"w") as f:
ll = formula_ltx
ll = ll.replace("FORMULA",formula)
ll = ll.replace("TITLE",title)
f.write(ll)
# compile formula
CMD = """
cd formulas ;
pdflatex {TEX} ;
convert -trim -density 300 {PDF} -quality 90 -colorspace RGB {PNG} ;
""".format(TEX=TEX,PDF=PDF,PNG=PNG)
os.system(CMD)
buf_nodes = ""
buf_edges = ""
def dfs_tree(x):
global buf_nodes, buf_edges
arg = ("" if x.arg is None else x.arg)
rv = ("" if x.retval is None else x.retval)
arg = arg.replace("\r","")
rv = rv.replace("\r","")
formula = arg + "\\Rightarrow " + rv
print(x.func_name + " -> " + x.arg_ascii + " -> " + x.retval_ascii)
x.func_name = x.func_name.replace("_","\\_")
write_formula("",str(x.uid),formula,x.func_name)
buf_nodes += """
{0} [image="{0}.png" label=""];
""".format(x.uid);
for y in x.children:
buf_edges += "{0} -> {1};\n".format(x.uid,y.uid);
dfs_tree(y)
dfs_tree(call_tree_root)
g = open(OUT_DIR + "/graph.dot", "w")
g.write("digraph g{")
g.write(buf_nodes)
g.write(buf_edges)
g.write("}\n")
g.close()
os.system("""cd formulas ; dot -Tpng graph.dot > graph.png ;""")
Mapping SymPy logic to differentiation rules
I think one remaining step is to map intermediary nodes from SymPy to differentiation rules. Here's some of the ones I was able to map:
- Product rule maps to sympy.core.mul.Mul._eval_derivative
- Chain rule maps to sympy.core.function.Function._eval_derivative
- Sum rule maps to sympy.core.add.Add._eval_derivative
- Derivative of a summation maps to sympy.concrete.summations.Sum._eval_derivative
- n-th Derivative of a product via the General Leibniz rule maps to sympy.core.mul.Mul._eval_derivative_n_times
- Generalized power rule maps to sympy.core.power.Pow._eval_derivative
I haven't seen a Fraction class in sympy.core
so maybe the quotient rule is handled indirectly through a product rule, and a generalized power rule with exponent -1.
Running
In order to get this to run you'll need:
sudo apt-get install graphviz imagemagick texlive texlive-latex-base
And the file /etc/ImageMagick-6/policy.xml
will have to be updated with the following line to allow conversion fron PDF->PNG
:
<policy domain="coder" rights="read|write" pattern="PDF" />
There's another call graph library called jonga but it's a bit generic and doesn't allow to completely filter out unwanted calls.
这篇关于用sympy逐步区分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!