Mathematical equation manipulation in Python

后端 未结 5 1242
天命终不由人
天命终不由人 2020-12-05 03:36

I want to develop a GUI application which displays a given mathematical equation. When you click upon a particular variable in the equation to signify that it is the unknown

相关标签:
5条回答
  • 2020-12-05 03:53

    Things have sure changed since 2009. I don't know how your GUI application is going, but this is now possible directly in IPython qtconsole (which one could embed inside a custom PyQt/PySide application, and keep track of all the defined symbols, to allow GUI interaction in a separate listbox, etc.)

    enter image description here

    (Uses the sympyprt extension for IPython)

    0 讨论(0)
  • 2020-12-05 03:56

    Sage has support for symbolic math. You could just use some of the equation manipulating functions built-in:

    http://sagemath.org/

    0 讨论(0)
  • 2020-12-05 04:04

    If you want to do this out of the box, without relying on librairies, I think that the problems you will find are not Python related. If you want to find such equations, you have to describe the heuristics necessary to solve these equations.

    First, you have to represent your equation. What about separating:

    • operands:
      • symbolic operands (a,b)
      • numeric operands (1,2)
    • operators:
      • unary operators (-, trig functions)
      • binary operators (+,-,*,/)

    Unary operators will obviously enclose one operand, binary ops will enclose two.

    What about types?

    I think that all of these components should derivate from a single common expression type. And this class would have a getsymbols method to locate quickly symbols in your expressions.

    And then distinguish between unary and binary operators, add a few basic complement/reorder primitives...

    Something like:

    class expression(object):
        def symbols(self):
            if not hasattr(self, '_symbols'):
                self._symbols = self._getsymbols()
            return self._symbols
        def _getsymbols(self):
            """
            return type: list of strings
            """
            raise NotImplementedError
    
    class operand(expression): pass
    
    class symbolicoperand(operand):
        def __init__(self, name):
            self.name = name
        def _getsymbols(self):
            return [self.name]
        def __str__(self):
            return self.name
    
    class numericoperand(operand):
        def __init__(self, value):
            self.value = value
        def _getsymbols(self):
            return []
        def __str__(self):
            return str(self.value)
    
    class operator(expression): pass
    
    class binaryoperator(operator):
        def __init__(self, lop, rop):
            """
            @type lop, rop: expression
            """
            self.lop = lop
            self.rop = rop
        def _getsymbols(self):
            return self.lop._getsymbols() + self.rop._getsymbols()
        @staticmethod
        def complementop():
            """
            Return complement operator:
             op.complementop()(op(a,b), b) = a
            """
            raise NotImplementedError
        def reorder():
            """
            for op1(a,b) return op2(f(b),g(a)) such as op1(a,b) = op2(f(a),g(b))
            """
            raise NotImplementedError
        def _getstr(self):
            """
            string representing the operator alone
            """
            raise NotImplementedError
        def __str__(self):
            lop = str(self.lop)
            if isinstance(self.lop, operator):
                lop = '(%s)' % lop
            rop = str(self.rop)
            if isinstance(self.rop, operator):
                rop = '(%s)' % rop
            return '%s%s%s' % (lop, self._getstr(), rop)
    
    
    class symetricoperator(binaryoperator): 
        def reorder(self):
            return self.__class__(self.rop, self.lop)
    
    class asymetricoperator(binaryoperator):
        @staticmethod
        def _invert(operand):
            """
            div._invert(a) -> 1/a
            sub._invert(a) -> -a
            """
            raise NotImplementedError
    
        def reorder(self):
            return self.complementop()(self._invert(self.rop), self.lop)
    
    
    class div(asymetricoperator):
        @staticmethod
        def _invert(operand):
            if isinstance(operand, div):
                return div(self.rop, self.lop)
            else:
                return div(numericoperand(1), operand)
        @staticmethod
        def complementop():
            return mul
        def _getstr(self):
            return '/'
    
    class mul(symetricoperator):
        @staticmethod
        def complementop():
            return div
        def _getstr(self):
            return '*'
    
    class add(symetricoperator):
        @staticmethod
        def complementop():
            return sub
        def _getstr(self):
            return '+'
    
    class sub(asymetricoperator):
        @staticmethod
        def _invert(operand):
            if isinstance(operand, min):
                return operand.op
            else:
                return min(operand)
        @staticmethod
        def complementop():
            return add
        def _getstr(self):
            return '-'
    
    class unaryoperator(operator):
        def __init__(self, op):
            """
            @type op: expression
            """
            self.op = op
        @staticmethod
        def complement(expression):
            raise NotImplementedError
    
        def _getsymbols(self):
            return self.op._getsymbols()
    
    class min(unaryoperator):
        @staticmethod
        def complement(expression):
            if isinstance(expression, min):
                return expression.op
            else:
                return min(expression) 
        def __str__(self):
            return '-' + str(self.op)
    

    With this basic structure set up, you should be able to describe a simple heuristic to solve very simple equations. Just think of the simple rules you learned to solve equations, and write them down. That should work :)

    And then a very naive solver:

    def solve(left, right, symbol):
        """
        @type left, right: expression
        @type symbol: string
        """
        if symbol not in left.symbols():
            if symbol not in right.symbols():
                raise ValueError('%s not in expressions' % symbol)
            left, right = right, left
    
        solved = False
        while not solved:
            if isinstance(left, operator):
                if isinstance(left, unaryoperator):
                    complementor = left.complement
                    right = complementor(right)
                    left = complementor(left)
                elif isinstance(left, binaryoperator):
                    if symbol in left.rop.symbols():
                        left = left.reorder()
                    else:
                        right = left.complementop()(right, left.rop)
                        left = left.lop
            elif isinstance(left, operand): 
                assert isinstance(left, symbolicoperand)
                assert symbol==left.name
                solved = True
    
        print symbol,'=',right
    
    a,b,c,d,e = map(symbolicoperand, 'abcde')
    
    solve(a, div(add(b,mul(c,d)),e), 'd') # d = ((a*e)-b)/c
    solve(numericoperand(1), min(min(a)), 'a') # a = 1
    
    0 讨论(0)
  • 2020-12-05 04:11

    Using SymPy, your example would go something like this:

    >>> import sympy
    >>> a,b,c,d,e = sympy.symbols('abcde')
    >>> r = (b+c*d)/e
    >>> l = a
    >>> r = sympy.solve(l-r,d)
    >>> l = d
    >>> r
    [(-b + a*e)/c]
    >>> 
    

    It seems to work for trigonometric functions too:

    >>> l = a
    >>> r = b*sympy.sin(c)
    >>> sympy.solve(l-r,c)
    [asin(a/b)]
    >>> 
    

    And since you are working with a GUI, you'll (probably) want to convert back and forth from strings to expressions:

    >>> r = '(b+c*d)/e'
    >>> sympy.sympify(r)
    (b + c*d)/e
    >>> sympy.sstr(_)
    '(b + c*d)/e'
    >>> 
    

    or you may prefer to display them as rendered LaTeX or MathML.

    0 讨论(0)
  • 2020-12-05 04:16

    What you want to do isn't easy. Some equations are quite straight forward to rearrange (like make b the subject of a = b*c+d, which is b = (a-d)/c), while others are not so obvious (like make x the subject of y = x*x + 4*x + 4), while others are not possible (especially when you trigonometric functions and other complications).

    As other people have said, check out Sage. It does what you want:

    You can solve equations for one variable in terms of others:
    
    sage: x, b, c = var('x b c')
    sage: solve([x^2 + b*x + c == 0],x)
    [x == -1/2*b - 1/2*sqrt(b^2 - 4*c), x == -1/2*b + 1/2*sqrt(b^2 - 4*c)]
    
    0 讨论(0)
提交回复
热议问题