7

I have input of a whole lot of math expressions and equations, and I'd like to print out latex representation for each on them. So far I have tried Sage and sympy, but the tricky part is to not-reorder terms in expressions.

So, if my input is this, something that can be eval-ed in python:

(C - A*x) / B

I want output that will be something like this:

\frac{C - A x}{B}

What I don't want is something like this:

\frac{-(A x - C)}{B}
\frac{1}{B}(C - A x)
etc...

Can this be achieved? I'm slowly losing hope...


EDIT:

The input expressions are diverse, some containing square roots, nested parentheses, exponents etc. Looking for a generic solution.

Here is what doesn't work so far:

1) Sage:

sage: var('A B C x y') 
(A, B, C, x, y) 
sage: latex(y == (C - A*x) / B)
y = -\frac{A x - C}{B}

2) sympy:

>>> from sympy import *
>>> x = Symbol('x')
>>> A = Symbol('A')
>>> B = Symbol('B')
>>> C = Symbol('C')
>>> latex((C - A*x) / B)
'\\frac{1}{B} \\left(- A x + C\\right)'
frnhr
  • 12,354
  • 9
  • 63
  • 90

2 Answers2

3

Short of writing your own parser, I believe the only real way to do this is to use python's built-in compile() function and process the returned abstract syntax tree.

Jim Garrison
  • 4,199
  • 4
  • 25
  • 39
1

\You can do this by creating Symbol and Operator classes that implement the standard python data model (http://docs.python.org/2/reference/datamodel.html). This will keep things in the same order of python operator precedence, although you can rearrange via parens:

class Symbol(object):
    def __init__(self, name):
        self._name = name

    def __str__(self):
        return str(self._name)

    def __div__(self, other):
        return Div(self, other)

    def __mul__(self, other):
        return Mult(self, other)

    def __add__(self, other):
        return Add(self, other)

    def __sub__(self, other):
        return Sub(self, other)

    def __rdiv__(self, other):
        return Div(other, self)

    def __rmul__(self, other):
        return Mult(other, self)

    def __radd__(self, other):
        return Add(other, self)

    def __rsub__(self, other):
        return Sub(other, self)

class Operation(Symbol):
    def __init__(self, a, b, op):
        self._a = a
        self._b = b
        self._op = op

    def __str__(self):
        return self._op.format(self._a, self._b)

class Add(Operation):
    precedence = 0

    def __init__(self, a, b):
        super(Add, self).__init__(a, b, "{0} + {1}")

class Sub(Operation):
    precedence = 0
    def __init__(self, a, b):
        super(Sub, self).__init__(a, b, "{0} - {1}")

class Mult(Operation):
    precedence = 1
    def __init__(self, a, b):
        if isinstance(a, Operation) and a.precedence < Mult.precedence:
            a_form = "({0})"
        else:
            a_form = "{0}"
        if isinstance(b, Operation) and b.precedence < Mult.precedence:
            b_form = "({1})"
        else:
            b_form = "{1}"
        super(Mult, self).__init__(a, b, a_form + " " + b_form)

class Div(Operation):
    precedence = 1
    def __init__(self, a, b):
        super(Div, self).__init__(a, b, "\\frac{{{0}}}{{{1}}}")


A = Symbol('A')
B = Symbol('B')
C = Symbol('C')
x = Symbol('x')

Then:

>>> print (C - A * x) / (B)
\frac{C - A x}{B}
>>> print (C * (A + B))
C (A + B)
>>> print (C * (A + B + A + B + C + x))
C (A + B + A + B + C + x)
John Spong
  • 1,361
  • 7
  • 8
  • This won't preserve parentheses in something like `x * (y+z)`. – jwodder Oct 30 '13 at 21:27
  • True. This is a quick-and-dirty example of the data model. To preserve parentheses, you would need to maintain more data in your combination methods to handle order-of-operations. – John Spong Oct 30 '13 at 21:32
  • Edited to maintain operator precedence – John Spong Oct 30 '13 at 21:46
  • @JohnSpong This looks nice, but it seems to be too specific - I'm looking for a general solution that will support a vide range of math expressions (exponents, roots, nested stuff etc.) – frnhr Oct 30 '13 at 21:54
  • Depending on your use case, you'll either need to go this route, or accept the argument re-ordering of a third-party library. http://docs.python.org/2/reference/datamodel.html#emulating-numeric-types should provide almost all of that stuff, and the precedence mechanism should already be half-way there. – John Spong Oct 30 '13 at 21:59
  • @JohnSpong Yeah I guess you are right :) Care to shortly compare your approach to the other answer by JimGarrison, i.e. this one: http://stackoverflow.com/a/3874621/236195 ? Is there a way way to support equations with your approach? – frnhr Oct 30 '13 at 23:39
  • At a quick glance, I would say that the AST version is more convenient to use, since it should work for any variable or literal without having to explicitly instantiate the class. The class-based version that I proposed is arguably more "pythonic", and is guaranteed to behave the same way as the python order-of-operations without having to reinvent that wheel. I'm sure that in practice, you'll find more cases where one falls short of the other, but I would anticipate that using the data model is more flexible in the long-term. – John Spong Oct 30 '13 at 23:52
  • "Is there a way to support equations with your approach?" Overriding the "rich comparison" methods (http://docs.python.org/2/reference/datamodel.html#object.__lt__) should provide this. – John Spong Oct 31 '13 at 00:06
  • "Not reinventing the wheel" is the thing that makes me prefer this solution. I guess that parsing the expression for variables and instantiating them automatically will be relatively pain-free :) – frnhr Oct 31 '13 at 01:39