Дифференциальный оператор, используемый в матричной форме, в модуле Python Sympy

Нам нужны две матрицы дифференциальных операторов [B] и [C], такие как:

B = sympy.Matrix([[ D(x), D(y) ],
                  [ D(y), D(x) ]])

C = sympy.Matrix([[ D(x), D(y) ]])

ans = B * sympy.Matrix([[x*y**2],
                        [x**2*y]])
print ans
[x**2 + y**2]
[      4*x*y]

ans2 = ans * C
print ans2
[2*x, 2*y]
[4*y, 4*x]

Это также может быть применено для вычисления завитка векторного поля, подобного:

culr  = sympy.Matrix([[ D(x), D(y), D(z) ]])
field = sympy.Matrix([[ x**2*y, x*y*z, -x**2*y**2 ]])

Чтобы решить эту проблему с помощью Sympy, необходимо создать следующий класс Python:

import sympy

class D( sympy.Derivative ):
    def __init__( self, var ):
        super( D, self ).__init__()
        self.var = var

    def __mul__(self, other):
        return sympy.diff( other, self.var )

Этот класс сам по себе решает, когда матрица дифференциальных операторов умножается слева. Здесь diff выполняется только тогда, когда известна дифференцируемая функция.

Чтобы обойти, когда матрица дифференциальных операторов умножается справа, метод __mul__ в базовом классе Expr должен был быть изменен следующим образом:

class Expr(Basic, EvalfMixin):
    # ...
    def __mul__(self, other):
        import sympy
        if other.__class__.__name__ == 'D':
            return sympy.diff( self, other.var )
        else:
            return Mul(self, other)
    #...

Это работает очень хорошо, но в Sympy должно быть лучшее собственное решение для Sympy. Кто-нибудь знает, что это может быть?

Ответ 1

Это решение применяет советы от других ответов и отсюда. Оператор D может быть определен следующим образом:

  • рассматривается только при умножении слева, так что D(t)*2*t**3 = 6*t**2, но 2*t**3*D(t) ничего не делает
  • все выражения и символы, используемые с D, должны иметь is_commutative = False
  • оценивается в контексте данного выражения с использованием evaluateExpr()
    • который идет справа налево по выражению, нахо- дящему операторы D и применяя mydiff() * к соответствующей правой части

*: mydiff используется вместо diff, чтобы создать более высокий порядок D, например mydiff(D(t), t) = D(t,t)

diff внутри __mul__() в D хранился только для справки, так как в текущем решении evaluateExpr() фактически выполняет задание дифференцирования. Мудул python был создан и сохранен как d.py.

import sympy
from sympy.core.decorators import call_highest_priority
from sympy import Expr, Matrix, Mul, Add, diff
from sympy.core.numbers import Zero

class D(Expr):
    _op_priority = 11.
    is_commutative = False
    def __init__(self, *variables, **assumptions):
        super(D, self).__init__()
        self.evaluate = False
        self.variables = variables

    def __repr__(self):
        return 'D%s' % str(self.variables)

    def __str__(self):
        return self.__repr__()

    @call_highest_priority('__mul__')
    def __rmul__(self, other):
        return Mul(other, self)

    @call_highest_priority('__rmul__')
    def __mul__(self, other):
        if isinstance(other, D):
            variables = self.variables + other.variables
            return D(*variables)
        if isinstance(other, Matrix):
            other_copy = other.copy()
            for i, elem in enumerate(other):
                other_copy[i] = self * elem
            return other_copy

        if self.evaluate:
            return diff(other, *self.variables)
        else:
            return Mul(self, other)

    def __pow__(self, other):
        variables = self.variables
        for i in range(other-1):
            variables += self.variables
        return D(*variables)

def mydiff(expr, *variables):
    if isinstance(expr, D):
        expr.variables += variables
        return D(*expr.variables)
    if isinstance(expr, Matrix):
        expr_copy = expr.copy()
        for i, elem in enumerate(expr):
            expr_copy[i] = diff(elem, *variables)
        return expr_copy
    return diff(expr, *variables)

def evaluateMul(expr):
    end = 0
    if expr.args <> ():
        if isinstance(expr.args[-1], D):
            if len(expr.args[:-1])==1:
                cte = expr.args[0]
                return Zero()
            end = -1
    for i in range(len(expr.args)-1+end, -1, -1):
        arg = expr.args[i]
        if isinstance(arg, Add):
            arg = evaluateAdd(arg)
        if isinstance(arg, Mul):
            arg = evaluateMul(arg)
        if isinstance(arg, D):
            left = Mul(*expr.args[:i])
            right = Mul(*expr.args[i+1:])
            right = mydiff(right, *arg.variables)
            ans = left * right
            return evaluateMul(ans)
    return expr

def evaluateAdd(expr):
    newargs = []
    for arg in expr.args:
        if isinstance(arg, Mul):
            arg = evaluateMul(arg)
        if isinstance(arg, Add):
            arg = evaluateAdd(arg)
        if isinstance(arg, D):
            arg = Zero()
        newargs.append(arg)
    return Add(*newargs)

def evaluateExpr(expr):
    if isinstance(expr, Matrix):
        for i, elem in enumerate(expr):
            elem = elem.expand()
            expr[i] = evaluateExpr(elem)
        return expr
    expr = expr.expand()
    if isinstance(expr, Mul):
        expr = evaluateMul(expr)
    elif isinstance(expr, Add):
        expr = evaluateAdd(expr)
    elif isinstance(expr, D):
        expr = Zero()
    return expr

Пример 1: завиток векторного поля. Обратите внимание, что важно определить переменные с commutative=False, так как их порядок в Mul().args повлияет на результаты, см. этот другой вопрос.

from d import D, evaluateExpr
from sympy import Matrix
sympy.var(x, y, z, commutative=False)
curl  = Matrix( [[ D(x), D(y), D(z) ]] )
field = Matrix( [[ x**2*y, x*y*z, -x**2*y**2 ]] )       
evaluateExpr( curl.cross( field ) )
# [-x*y - 2*x**2*y, 2*x*y**2, -x**2 + y*z]

Пример 2: Типичное приближение Ритца, используемое в структурном анализе.

from d import D, evaluateExpr
from sympy import sin, cos, Matrix
sin.is_commutative = False
cos.is_commutative = False
g1 = []
g2 = []
g3 = []
var('x,t,r,A', commutative=False)
m=5
n=5
for j in xrange(1,n+1):
    for i in xrange(1,m+1):
        g1 += [sin(i*x)*sin(j*t),                 0,                 0]
        g2 += [                0, cos(i*x)*sin(j*t),                 0]
        g3 += [                0,                 0, sin(i*x)*cos(j*t)]
g = Matrix( [g1, g2, g3] )

B = Matrix(\
    [[     D(x),        0,        0],
     [    1/r*A,        0,        0],
     [ 1/r*D(t),        0,        0],
     [        0,     D(x),        0],
     [        0,    1/r*A, 1/r*D(t)],
     [        0, 1/r*D(t), D(x)-1/x],
     [        0,        0,        1],
     [        0,        1,        0]])

ans = evaluateExpr(B*g)

Функция A print_to_file() создана для быстрой проверки больших выражений.

import sympy
import subprocess
def print_to_file( guy, append=False ):
    flag = 'w'
    if append: flag = 'a'
    outfile = open(r'print.txt', flag)
    outfile.write('\n')
    outfile.write( sympy.pretty(guy, wrap_line=False) )
    outfile.write('\n')
    outfile.close()
    subprocess.Popen( r'notepad.exe print.txt' )

print_to_file( B*g )
print_to_file( ans, append=True )

Ответ 2

Дифференциальные операторы не существуют в ядре SymPy, и даже если они существовали, "умножение на оператор" вместо "применения оператора" - это злоупотребление нотацией, которое не поддерживается SymPy.

[1] Еще одна проблема заключается в том, что выражения SymPy могут быть построены только из подклассов sympy.Basic, поэтому вероятно, что ваш class D просто вызывает ошибку при вводе как sympy_expr+D(z). Вот почему (expression*D(z)) * (another_expr) терпит неудачу. (expression*D(z)) не может быть построен.

Кроме того, если аргумент D не является одним Symbol, неясно, чего вы ожидаете от этого оператора.

Наконец, diff(f(x), x) (где f является символьной неизвестной функцией) возвращает неоцененные выражения, как вы заметили просто потому, что, когда f неизвестно, нет ничего, что могло бы разумно вернуться. Позже, когда вы замените expr.subs(f(x), sin(x)), производная будет оценена (в худшем случае вам может потребоваться позвонить expr.doit()).

[2] В вашей проблеме нет элегантного и. Способ, который я предложил бы для решения вашей проблемы, - это переопределить метод __mul__ Expr: вместо того, чтобы просто умножать деревья выражений, он проверяет, содержит ли дерево левого выражения экземпляры D, и оно будет применять их. Очевидно, что это не масштабируется, если вы хотите добавить новые объекты. Это давняя известная проблема с дизайном sympy.

EDIT: [1] необходимо просто разрешить создание выражений, содержащих D. [2] необходим для выражений, содержащих нечто большее, чем только один D.

Ответ 3

Если вы хотите, чтобы правильное умножение работало, вам нужно подклассом всего лишь object. Это приведет к тому, что x*D вернется к D.__rmul__. Я не могу себе представить, что это высокий приоритет, поскольку операторы никогда не применяются справа.

Ответ 4

Создание оператора, который работает автоматически всегда, в настоящее время невозможно. Чтобы действительно работать полностью, вам понадобится http://code.google.com/p/sympy/issues/detail?id=1941. См. Также https://github.com/sympy/sympy/wiki/Canonicalization (не стесняйтесь редактировать эту страницу).

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

Кстати, одно дело с дифференциальным оператором как "умножение" состоит в том, что оно неассоциативно. А именно, (D*f)*g= g*Df, тогда как D*(f*g)= g*Df + f*Dg. Поэтому вам нужно быть осторожным, когда вы делаете что-то, что оно не "съедает" часть выражения, а не все. Например, D*2*x дал бы 0 из-за этого. SymPy везде предполагает, что умножение ассоциативно, поэтому оно может сделать это неправильно в какой-то момент.

Если это станет проблемой, я бы рекомендовал сбросить автоматическое приложение и просто работать с функцией, которая проходит и применяет ее (что, как я уже отмечал выше, вам все равно понадобится).