Подтвердить что ты не робот

Преобразование числового выражения python в LaTeX

Мне нужно преобразовать строки с допустимым синтаксисом python, например:

'1+2**(x+y)'

и получить эквивалент LaTeX:

$1+2^{x+y}$

Я пробовал функцию симплексного латекса, но он обрабатывает фактическое выражение, а не его строчную форму:

>>> latex(1+2**(x+y))
'$1 + 2^{x + y}$'
>>> latex('1+2**(x+y)')
'$1+2**(x+y)$'

но даже для этого требуется, чтобы x и y были объявлены как символы типа.

Я хочу что-то более прямолинейное, желательно выполнимое с парсером из модуля компилятора.

>>> compiler.parse('1+2**(x+y)')
Module(None, Stmt([Discard(Add((Const(1), Power((Const(2), Add((Name('x'), Name('y'))))))))]))

И последнее, но не менее важное: почему мне нужно генерировать эти латексные snipptes, чтобы я мог показать их на веб-странице с помощью mathjax.

4b9b3361

Ответ 1

Вы можете использовать sympy.latex с eval:

s = "1+2**(x+y)"
sympy.latex(eval(s))   # prints '$1 + {2}^{x + y}$'

Вам все равно придется объявлять переменные как символы, но если это действительно проблема, гораздо проще написать парсер для этого, чем анализировать все и генерировать латекс с нуля.

Ответ 2

Здесь довольно длинный, но все еще неполный метод, который никак не связан с sympy. Этого достаточно, чтобы охватить пример (-b-sqrt(b**2-4*a*c))/(2*a), который переводится на \frac{- b - \sqrt{b^{2} - 4 \; a \; c}}{2 \; a} и отображается как

alt text

В основном он создает АСТ и идет по нему, создавая математику латекса, соответствующую узлам АСТ. Что должно дать достаточно идеи о том, как продлить его в тех местах, где ему не хватает.


import ast

class LatexVisitor(ast.NodeVisitor):

    def prec(self, n):
        return getattr(self, 'prec_'+n.__class__.__name__, getattr(self, 'generic_prec'))(n)

    def visit_Call(self, n):
        func = self.visit(n.func)
        args = ', '.join(map(self.visit, n.args))
        if func == 'sqrt':
            return '\sqrt{%s}' % args
        else:
            return r'\operatorname{%s}\left(%s\right)' % (func, args)

    def prec_Call(self, n):
        return 1000

    def visit_Name(self, n):
        return n.id

    def prec_Name(self, n):
        return 1000

    def visit_UnaryOp(self, n):
        if self.prec(n.op) > self.prec(n.operand):
            return r'%s \left(%s\right)' % (self.visit(n.op), self.visit(n.operand))
        else:
            return r'%s %s' % (self.visit(n.op), self.visit(n.operand))

    def prec_UnaryOp(self, n):
        return self.prec(n.op)

    def visit_BinOp(self, n):
        if self.prec(n.op) > self.prec(n.left):
            left = r'\left(%s\right)' % self.visit(n.left)
        else:
            left = self.visit(n.left)
        if self.prec(n.op) > self.prec(n.right):
            right = r'\left(%s\right)' % self.visit(n.right)
        else:
            right = self.visit(n.right)
        if isinstance(n.op, ast.Div):
            return r'\frac{%s}{%s}' % (self.visit(n.left), self.visit(n.right))
        elif isinstance(n.op, ast.FloorDiv):
            return r'\left\lfloor\frac{%s}{%s}\right\rfloor' % (self.visit(n.left), self.visit(n.right))
        elif isinstance(n.op, ast.Pow):
            return r'%s^{%s}' % (left, self.visit(n.right))
        else:
            return r'%s %s %s' % (left, self.visit(n.op), right)

    def prec_BinOp(self, n):
        return self.prec(n.op)

    def visit_Sub(self, n):
        return '-'

    def prec_Sub(self, n):
        return 300

    def visit_Add(self, n):
        return '+'

    def prec_Add(self, n):
        return 300

    def visit_Mult(self, n):
        return '\\;'

    def prec_Mult(self, n):
        return 400

    def visit_Mod(self, n):
        return '\\bmod'

    def prec_Mod(self, n):
        return 500

    def prec_Pow(self, n):
        return 700

    def prec_Div(self, n):
        return 400

    def prec_FloorDiv(self, n):
        return 400

    def visit_LShift(self, n):
        return '\\operatorname{shiftLeft}'

    def visit_RShift(self, n):
        return '\\operatorname{shiftRight}'

    def visit_BitOr(self, n):
        return '\\operatorname{or}'

    def visit_BitXor(self, n):
        return '\\operatorname{xor}'

    def visit_BitAnd(self, n):
        return '\\operatorname{and}'

    def visit_Invert(self, n):
        return '\\operatorname{invert}'

    def prec_Invert(self, n):
        return 800

    def visit_Not(self, n):
        return '\\neg'

    def prec_Not(self, n):
        return 800

    def visit_UAdd(self, n):
        return '+'

    def prec_UAdd(self, n):
        return 800

    def visit_USub(self, n):
        return '-'

    def prec_USub(self, n):
        return 800
    def visit_Num(self, n):
        return str(n.n)

    def prec_Num(self, n):
        return 1000

    def generic_visit(self, n):
        if isinstance(n, ast.AST):
            return r'' % (n.__class__.__name__, ', '.join(map(self.visit, [getattr(n, f) for f in n._fields])))
        else:
            return str(n)

    def generic_prec(self, n):
        return 0

def py2tex(expr):
    pt = ast.parse(expr)
    return LatexVisitor().visit(pt.body[0].value)

Ответ 3

Вы можете использовать SymPy. Просто сначала передайте строку в функцию sympify(), которая преобразует ее в действительное выражение SymPy (т.е. Создаст символы для вас и т.д.). Таким образом, вы могли бы сделать

>>> latex(sympify('1+2**(x+y)'))
1 + 2^{x + y}

S() также является ярлыком для sympify(), т.е. latex(S('1+2**(x+y)')) также работает.

Ответ 4

Просто немного исправьте Джеффа Риди отличный ответ:

class GenerateSymbols(defaultdict):
    def __missing__(self, key):
        self[key] = sympy.Symbol(key)
        return self[key]

Прежде чем он не добавит новый элемент в dict. Теперь вы можете использовать это с вашим выражением:

d= GenerateSymbols()    
eq = '(-b-sqrt(b**2-4*a*c))/(2*a)'

и вы можете еще больше упростить его, прежде чем преобразовать его в LaTeX:

sympy.latex(sympy.simplify(eval(eq,d)))

и вы получите следующее:

'$- \\frac{b + \\operatorname{sqrt}\\left(- 4 a c + b^{2}\\right)}{2 a}$'

Ответ 5

Чтобы основываться на ответе tom10, вы можете определить словарь, который будет генерировать символы и использовать его при вызове eval:

from collections import defaultdict
class GenerateSymbols(defaultdict):
  def __missing__(self, key):
    return sympy.Symbol(key)

Затем, если вы используете

sympy.latex(eval('1+2**(x+y)',GenerateSymbols()))

вам не нужно беспокоиться о предварительном создании символов для переменных.