有没有办法在SymPy中一步一步地得到解决方案?例如:
x**2-5 = 4
step 1 x**2-5+5=4+5
step 2 : x**2=9
step 3 :x = 3 or x= -3发布于 2016-12-19 16:05:01
(这更多是作为回答的评论)
有一些谷歌的-soc的想法来实现
逐步表达式可视化
2014年GSoC创意:很多时候,人们都会问他们如何知道某些函数在做什么。例如,他们想一步一步地了解.对于前者,您能做的最好的是遵循代码;对于后者,算法根本不像您手工完成的那样工作,所以真的没有办法.
分步策略
许多SymPy操作背后的逻辑被分成几个小方法。例如,像sin或exp这样的对象有_eval_derivative方法,它们作为SymPy来计算复杂表达式的导数,比如sin(exp(x))。通过捕获所有这些小方法的输入和输出,我们可以收集大量关于SymPy采取的步骤的信息。我们可以看到,exp._eval_derivative接受exp(x)并返回exp(x),sin._eval_derivative接受sin(exp(x))和返回的cos(exp(x))*exp(x)。每个方法的这些输入输出对可能足以说明SymPy如何解决许多领域的问题。
这种捕获许多内部函数输入的方法类似于传统的用于分析大型代码库的日志记录系统。我们应该调查它们是如何工作的,以及它们是否会对正常运行造成任何问题。
一旦这个信息源可用,我们就可以想出有趣的可视化方法和与之交互的方法。一个好的解决方案不会不可避免地将数据流与特定的可视化技术联系在一起。
这种方法在智力上是直截了当的,但可能需要学生与大量代码库进行交互。像_eval_derivative这样的方法在整个SymPy中随处可见,但在不同的模块中往往有很小的变化。
这里是一个在线解决方案SymPy伽玛
发布于 2021-11-05 07:44:49
简单案例的临时解决方案可能基于消去树。在开始时,您可以创建一个表达式树,而不需要计算。之后,您可以逐级修改结果级别:

源代码:
class TraverseSolver:
def __init__(self, expr):
self.expr = expr
def _set_graph(self):
self.G = nx.nx_agraph.from_agraph(pygraphviz.AGraph(sp.dotprint(self.expr)))
def _set_map(self):
self._map = dict(zip(self.G.nodes, sp.preorder_traversal(self.expr)))
def _set_baseNode(self):
self._baseNode = next(iter(self.G.nodes))
def get_levels(self, mode='draw'):
if mode == 'draw':
d = nx.single_source_shortest_path_length(self.G, self._baseNode)
u, idx = np.unique(list(d.values()), return_index=True)
levels = [[str(m) for m in n] for n in reversed(np.split(np.array(list(d.keys())), idx[1:]))]
return levels
elif mode == 'traverse':
print(self.G)
def set_color(self, node, color):
self.G.nodes[node]['color'] = color
def display_graph(self, fig, n, nshape=(2, 3)):
ax = fig.add_subplot(*nshape, n)
pos = graphviz_layout(self.G, prog='dot')
colors = nx.get_node_attributes(self.G, 'color')
nx.draw(self.G, pos = pos, nodelist=[])
# draw self.G bbox by bbox:
for i, n in enumerate(self.G.nodes()):
nx.draw(nx.subgraph(self.G, [n]), pos={n:pos[n]}, labels = {n:f'${sp.latex(self._map[n])}$'}, nodelist=[],
bbox=dict(facecolor=colors[n], edgecolor='black', boxstyle='round,pad=0.7'))
def solve(self, display_graph=True, nshape=(2, 3)):
self._set_graph() #store sp.srepr+code in each node
self._set_map() #sp.srepr+code -> expression (without evaluation)
self._set_baseNode() #sp.srepr+code of self.
solutionSteps = [self._map[self._baseNode]] #first step that contains initial expression
levels = self.get_levels(mode='draw')
if display_graph:
fig = plt.figure(figsize=(20,10))
#Step forward
for i in range(len(levels)):
if display_graph:
for node in self.G.nodes():
self.set_color(node, 'lightblue')
anyChanges = False
for activeNode in levels[i]:
beforeEval = self._map[activeNode]
if display_graph:
self.set_color(activeNode, 'yellow')
if not beforeEval.is_Atom:
afterEval = beforeEval.func(*beforeEval.args, evaluate=True) #is beforeEval different with afterEval
if beforeEval != afterEval:
self._map[activeNode] = afterEval
if display_graph:
self.set_color(activeNode, 'lime')
anyChanges = True
# Calculate value of baseNode() using changes, no evaluation
if anyChanges:
for j in range(i+1, len(levels)):
for editNode in levels[j]:
args = [self._map[node] for node in self.G[editNode]] #each ancestor
if not self._map[editNode].is_Atom:
self._map[editNode] = self._map[editNode].func(*args, evaluate=False)
solutionSteps.append(self._map[self._baseNode])
if display_graph:
self.display_graph(fig, n=len(solutionSteps), nshape=nshape)
plt.show()
return solutionSteps
expr = sp.sympify('-1*(2*3-5*7)', evaluate=False)
steps = TraverseSolver(expr).solve(display_graph=True, nshape=(2, 3))
print('INPUT:', sp.srepr(expr))
print('SOLUTION 1:', ' = '. join([str(step) for step in steps]))
print('SOLUTION 2:', ' = '. join([sp.StrPrinter(dict(order='none'))._print(step) for step in steps])) 输出:
INPUT: Mul(Integer(-1), Add(Mul(Integer(-1), Mul(Integer(5), Integer(7))), Mul(Integer(2), Integer(3))))
SOLUTION 1: -(-5*7 + 2*3) = -(-1*35 + 2*3) = -(-35 + 6) = -1*(-29) = 29
SOLUTION 2: -(2*3 - 5*7) = -(2*3 - 1*35) = -(6 - 35) = -1*(-29) = 29要求:networkx,matplotlib,python-graphviz
发布于 2022-04-25 15:29:08
关于graphviz的答案,下面是对源代码的更正。还请注意pygraphviz是pip中的pygraphviz:
import networkx as nx
import matplotlib
import pygraphviz
import sympy as sp
import numpy as np
from matplotlib import pyplot as plt
class TraverseSolver:
def __init__(self, expr):
self.expr = expr
def _set_graph(self):
self.G = nx.nx_agraph.from_agraph(pygraphviz.AGraph(sp.dotprint(self.expr)))
def _set_map(self):
self._map = dict(zip(self.G.nodes, sp.preorder_traversal(self.expr)))
def _set_baseNode(self):
self._baseNode = next(iter(self.G.nodes))
def get_levels(self, mode='draw'):
if mode == 'draw':
d = nx.single_source_shortest_path_length(self.G, self._baseNode)
u, idx = np.unique(list(d.values()), return_index=True)
levels = [[str(m) for m in n] for n in reversed(np.split(np.array(list(d.keys())), idx[1:]))]
return levels
elif mode == 'traverse':
print(self.G)
def set_color(self, node, color):
self.G.nodes[node]['color'] = color
def display_graph(self, fig, n, nshape=(2, 3)):
ax = fig.add_subplot(*nshape, n)
pos = nx.nx_pydot.graphviz_layout(self.G, prog='dot')
colors = nx.get_node_attributes(self.G, 'color')
nx.draw(self.G, pos = pos, nodelist=[])
# draw self.G bbox by bbox:
for i, n in enumerate(self.G.nodes()):
nx.draw(nx.subgraph(self.G, [n]), pos={n:pos[n]}, labels = {n:f'${sp.latex(self._map[n])}$'}, nodelist=[],
bbox=dict(facecolor=colors[n], edgecolor='black', boxstyle='round,pad=0.7'))
def solve(self, display_graph=True, nshape=(2, 3)):
self._set_graph() #store sp.srepr+code in each node
self._set_map() #sp.srepr+code -> expression (without evaluation)
self._set_baseNode() #sp.srepr+code of self.
solutionSteps = [self._map[self._baseNode]] #first step that contains initial expression
levels = self.get_levels(mode='draw')
if display_graph:
fig = plt.figure(figsize=(20,10))
#Step forward
for i in range(len(levels)):
if display_graph:
for node in self.G.nodes():
self.set_color(node, 'lightblue')
anyChanges = False
for activeNode in levels[i]:
beforeEval = self._map[activeNode]
if display_graph:
self.set_color(activeNode, 'yellow')
if not beforeEval.is_Atom:
afterEval = beforeEval.func(*beforeEval.args, evaluate=True) #is beforeEval different with afterEval
if beforeEval != afterEval:
self._map[activeNode] = afterEval
if display_graph:
self.set_color(activeNode, 'lime')
anyChanges = True
# Calculate value of baseNode() using changes, no evaluation
if anyChanges:
for j in range(i+1, len(levels)):
for editNode in levels[j]:
args = [self._map[node] for node in self.G[editNode]] #each ancestor
if not self._map[editNode].is_Atom:
self._map[editNode] = self._map[editNode].func(*args, evaluate=False)
solutionSteps.append(self._map[self._baseNode])
if display_graph:
self.display_graph(fig, n=len(solutionSteps), nshape=nshape)
plt.show()
return solutionSteps
expr = sp.simplify('-1*(2*3-5*7)', evaluate=False)
steps = TraverseSolver(expr).solve(display_graph=True, nshape=(2, 3))
print('INPUT:', sp.srepr(expr))
print('SOLUTION 1:', ' = '. join([str(step) for step in steps]))
print('SOLUTION 2:', ' = '. join([sp.StrPrinter(dict(order='none'))._print(step) for step in steps]))https://stackoverflow.com/questions/39359220
复制相似问题