5

Python で文字列から数式を単純化できるようにしたいと考えています。それを行うには、いくつかの「交換可能な」方法があります。そのための非可換関数はありますか?

sympyからのsympifyがいくつかの非可換ジョブを実行できることは知っています。ここに例を示します。

from sympy import *
x=Symbol('x',commutative=False)
y=Symbol('y',commutative=False)

print sympify(3*x*y - y*x - 2*x*y)

x y -y x と表示されますが、文字列に sympify を適用すると、つまり、

print sympify('3*x*y - y*x - 2*x*y')

結果は0です。

xyの非可換性を維持するために上記の文字列を単純化する方法はありますか?

私は誰かがすでにここでそれについて尋ねていることを発見しましたhttp://osdir.com/ml/python-sympy/2012-02/msg00250.htmlそして誰かが答えましたhttp://osdir.com/ml/python-sympy/2012 -02/msg00255.html、ただし、ソリューションは一般的に機能しないようです。

すぐに解決策がない場合は、自分でコーディングする必要があると思います。

4

2 に答える 2

4

シンボル x と y に制約があることを Sympy に伝える必要があります。これを行うには、Symbolそれらのインスタンスを作成し、それらのパラメーターを aslocalsに渡すだけsympifyです。

In [120]: x = sympy.Symbol('x', commutative=False)

In [121]: y = sympy.Symbol('y', commutative=False)

In [122]: sympy.sympify('3*x*y - y*x - 2*x*y', locals={'x':x, 'y':y})
Out[122]: x*y - y*x

プログラムでそれを行うために、SymPy は文字列式からシンボルを抽出するための便利な解析ツールをいくつか提供しています。重要なアイデアは、通常の評価では必要なものを抽出する能力を台無しにする可換性の仮定が行われるため、評価を抑制しなければならないということです。

In [155]: s = sympy.parsing.sympy_parser.parse_expr('3*x*y - y*x - 2*x*y', evaluate=False)

In [156]: s.atoms(sympy.Symbol)
Out[156]: {x, y}

Symbol残念なことに、既に作成された の想定状態を変更する直接的な方法はないようです。しかし、これらのシンボルを反復処理して、同じ名前と非可換仮定を持つシンボルの新しいコレクションを作成し、それをlocalsin に使用できますsympify

def non_commutative_sympify(expr_string):
    parsed_expr = sympy.parsing.sympy_parser.parse_expr(
        expr_string, 
        evaluate=False
    )

    new_locals = {sym.name:sympy.Symbol(sym.name, commutative=False)
                  for sym in parsed_expr.atoms(sympy.Symbol)}

    return sympy.sympify(expr_string, locals=new_locals)

たとえば、次のようになります。

In [184]: non_commutative_sympify('3*x*y - y*x - 2*x*y')
Out[184]: x*y - y*x

In [185]: non_commutative_sympify('x*y*z - y*z*x - 2*x*y*z + z*y*x')
Out[185]: -x*y*z - y*z*x + z*y*x
于 2015-08-23T18:21:58.210 に答える
0

これが私の解決策です。アルゴリズムについては、上記のコメントまたはコード内のコメントを参照してください。誰かがよりエレガントなコードを提供してくれれば幸いです。

"""
Created on Sat Aug 22 22:15:16 2015

@author: GnacikM
"""

from sympy import *
import re
import string

"""
names for variables in a list
"""
alpha = list(string.ascii_lowercase)
Alpha = list(string.ascii_uppercase)

"""
Creating symbols
"""
def symbol_commutativity(my_symbol, name, status):
    my_symbol = Symbol(str(name), commutative=status)
    return my_symbol

symbols_lower = []
for item in alpha:
    symbols_lower.append(symbol_commutativity(item, item, False))

symbols_upper = []
for item in Alpha:
    symbols_upper.append(symbol_commutativity(item, item, False))

"""
Transforming an infix expression to Reverse Polish Notation
http://andreinc.net/2010/10/05/converting-infix-to-rpn-shunting-yard-algorithm/
"""
#Associativity constants for operators
LEFT_ASSOC = 0
RIGHT_ASSOC = 1

#Supported operators
OPERATORS = {
    '+' : (0, LEFT_ASSOC),
    '-' : (0, LEFT_ASSOC),
    '*' : (5, LEFT_ASSOC),
    '/' : (5, LEFT_ASSOC),
    '%' : (5, LEFT_ASSOC),
    '^' : (10, RIGHT_ASSOC)
}

#Test if a certain token is operator
def isOperator(token):
    return token in OPERATORS.keys()

#Test the associativity type of a certain token
def isAssociative(token, assoc):
    if not isOperator(token):
        raise ValueError('Invalid token: %s' % token)
    return OPERATORS[token][1] == assoc

#Compare the precedence of two tokens
def cmpPrecedence(token1, token2):
    if not isOperator(token1) or not isOperator(token2):
        raise ValueError('Invalid tokens: %s %s' % (token1, token2))
    return OPERATORS[token1][0] - OPERATORS[token2][0]   


#Transforms an infix expression to RPN
def infixToRPN(tokens):
    out = []
    stack = []
    #For all the input tokens [S1] read the next token [S2]
    for token in tokens:
        if isOperator(token):
            # If token is an operator (x) [S3]
            while len(stack) != 0 and isOperator(stack[-1]):
                # [S4]
                if (isAssociative(token, LEFT_ASSOC) and cmpPrecedence(token, stack[-1]) <= 0) or (isAssociative(token, RIGHT_ASSOC) and cmpPrecedence(token, stack[-1]) < 0):
                    # [S5] [S6]
                    out.append(stack.pop())
                    continue
                break
            # [S7]
            stack.append(token)
        elif token == '(':
            stack.append(token) # [S8]
        elif token == ')':
            # [S9]
            while len(stack) != 0 and stack[-1] != '(':
                out.append(stack.pop()) # [S10]
            stack.pop() # [S11]
        else:
            out.append(token) # [S12]
    while len(stack) != 0:
        # [S13]
        out.append(stack.pop())
    return out

"""
Evaluating an expression in Reverse Polish Notation, an input is a list
http://danishmujeeb.com/blog/2014/12/parsing-reverse-polish-notation-in-python
"""

def parse_rpn(expression):

  stack = []

  for val in expression:
      if val in ['-', '+', '*', '/', '^']:
          op1 = stack.pop()
          if len(stack)==0:
              op2 = 0
          else:
              op2 = stack.pop()

          if val=='-': 
              result = op2 - op1
          elif val=='+': 
              result = op2 + op1
          elif val=='*': 
              result = op2 * op1
          elif val=='/': 
              result = op2 / op1
          elif val=='^':
              result =  op2 ** op1
          stack.append(result)
      else:
          stack.append(val)
  return stack

"""
Definition of my non-commutative sympify
"""
def nc_sympify(string):
    expression_list = re.findall(r"(-\number|\b\w*[\.]?\w+\b|[\(\)\+\*\-\/^])", string)

    """ Modifying expression_list to fix the issue with negative numbers """
    t = len(expression_list) 
    i=0
    while i<t:
        if len(expression_list[i])>1 and expression_list[i][0]=='-' and expression_list[i-1]!='(':
            new_list1 = expression_list[:i]
            if i<len(expression_list):
                new_list2 = expression_list[i+1:]
            else:
                new_list2 = []
            new_entry1 = expression_list[i][0]
            new_entry2 = expression_list[i][1:]
            expression_list[:] = new_list1 +[new_entry1] +[new_entry2]+new_list2 
            t = len(expression_list)
        i+=1
    """End of this modification """

    for i in xrange(len(expression_list)):
        if expression_list[i] in alpha:
            for j in range(len(alpha)):
                if expression_list[i] == alpha[j]:
                    expression_list[i] = symbols_lower[j]
        elif expression_list[i]  in Alpha:
            for k in xrange(len(Alpha)):
                if expression_list[i]  == Alpha[k]:
                    expression_list[i] = symbols_upper[k]
        elif expression_list[i]  not in ['-', '+', '*', '/', '(', ')', '^', ' ']:
            expression_list[i]  = float(expression_list[i] )
            if i>0 and expression_list[i].is_integer()==True and expression_list[i-1]!='/':
                expression_list[i]=int(expression_list[i])
            elif i==0 and expression_list[i].is_integer()==True:
                expression_list[i]=int(expression_list[i])

    output = infixToRPN(expression_list)

    return parse_rpn(output)[0]


print nc_sympify('3*x*y - y*x - 2*x*y')
于 2015-08-22T21:41:52.350 に答える