Nano Hash - криптовалюты, майнинг, программирование

Пошаговая дифференциация с sympy

Я пытаюсь создать программу на Python, чтобы найти производные и интегралы, а также показать, как это сделать. До сих пор я обнаружил, что есть функция integer_steps, которая возвращает использованные шаги, но я не нашел эквивалента для дифференцирования.

Кто-нибудь знает, есть ли аналог?

Если нет, есть ли у вас какие-либо идеи о том, как найти шаги, необходимые для поиска производной?


Ответы:


1

Способ 1 (ручной)

Глядя на код, производный класс здесь живет логика верхнего уровня. Это только часть верхнего уровня. С этого момента вычисления требуют вычисления производных различных узлов внутри дерева выражений.

Логика для каждого конкретного узла дерева выражений находится в методе _eval_derivative соответствующий каждому конкретному типу узла.

Это позволит вам добавить код к этим _eval_derivative методам, чтобы отслеживать весь процесс и находить все шаги.

Способ 2 (с использованием трассировщика)

В Python есть несколько пакетов трассировки. охотник на python, написанный @ionelmc на самом деле довольно хорош и хорошо подходит для этого варианта использования.

Среди многих других функций он позволяет устанавливать определенные обратные вызовы, когда функция начинает выполняться, и еще один, когда функция возвращает свое значение. На самом деле это именно то, что нам нужно.

Вот пример, который показывает, как это использовать (я запускал и тестировал это на Python 3.7.3, SymPy 1.7 и 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)

Таким образом, это позволяет выбрать, какие структуры данных мы хотим проверить, как мы хотим их распечатать, и это позволяет нам увидеть промежуточные этапы процесса дифференциации:

[...]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>

Если вы хотите также охватить функцию diff, вы можете изменить приведенный выше код и получить function_in=['_eval_derivative','diff'] . Таким образом можно посмотреть не только частичные результаты, но и вызов функции diff и возвращаемое ею значение.

Способ 3 (использование трассировщика, построение графа вызовов и его визуализация)

Используя graphviz, латекс и трассировщик (опять же, python-hunter), вы действительно можете увидеть граф вызовов более нагляден. Для рендеринга всех формул для каждого промежуточного шага требуется немного времени, потому что используется pdflatex (хотя я уверен, что для латекса есть более быстрые рендереры).

введите здесь описание изображения

Значение каждого узла имеет следующий формат:

function_name
argument => return_value

Кажется, есть несколько узлов diff, у которых аргумент равен возвращаемому значению, которое я не знаю, как объяснить в данный момент.

Диаграмма, вероятно, могла бы быть более полезной, если бы она каким-то образом указывала, где применяется каждое правило (я не могу придумать простого способа сделать это).

Вот код для этого тоже:

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 ;""")

Сопоставление логики SymPy с правилами дифференциации

Я думаю, что один оставшийся шаг — сопоставить промежуточные узлы из SymPy с правилами дифференциации. Вот некоторые из тех, что мне удалось сопоставить:

Я не видел класса Fraction в sympy.core, поэтому, возможно, частное правило обрабатывается косвенно через правило продукта и обобщенное правило мощности с показателем -1.

Бег

Чтобы запустить это, вам понадобится:

sudo apt-get install graphviz imagemagick texlive texlive-latex-base

И файл /etc/ImageMagick-6/policy.xml нужно будет обновить следующей строкой, чтобы разрешить преобразование из PDF->PNG:

<policy domain="coder" rights="read|write" pattern="PDF" />

Существует еще одна библиотека графиков вызовов, которая называется jonga, но она немного универсальна и не позволяет полностью отфильтровать нежелательные вызовы. звонки.

04.12.2020
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..