[IPython-User] tex magic: parsing calculations to tex

Johan Beke johanbeke@hotmail....
Sat Aug 25 04:51:24 CDT 2012


Hi all,

As I said before, I would like to have numerical input formulas parsed to tex. I've written a simple magic function 'tex' which does the trick for numerical expressions. For the moment it needs to be without assignment. 

The code:

import ast
from IPython.core.display import Latex
from IPython.core.magic import (Magics, magics_class, line_magic,
                                cell_magic, line_cell_magic)

@magics_class
class PrettyPrint(Magics):
    @line_magic
    def tex(self, line):
        #stmt = compile(line,'<string>','exec')
        #exec stmt
        result = eval(line)
        return Latex("$$"+self.py2tex(line)+" = "+str(result)+"$$")
        
    def py2tex(self, expr):
        pt = ast.parse(expr)
        return LatexVisitor().visit(pt.body[0].value)

class LatexVisitor(ast.NodeVisitor):
    # source: http://stackoverflow.com/questions/3867028/converting-a-python-numeric-expression-to-latex
    greekLetters = {'Alpha': '\\Alpha',
                     'Beta': '\\Beta',
                     'Chi': '\\Chi',
                     'Delta': '\\Delta',
                     'Epsilon': '\\Epsilon',
                     'Eta': '\\Eta',
                     'Gamma': '\\Gamma',
                     'Iota': '\\Iota',
                     'Kappa': '\\Kappa',
                     'Lambda': '\\Lambda',
                     'Mu': '\\Mu',
                     'Nu': '\\Nu',
                     'Omega': '\\Omega',
                     'Phi': '\\Phi',
                     'Pi': '\\Pi',
                     'Psi': '\\Psi',
                     'Rho': '\\Rho',
                     'Sigma': '\\Sigma',
                     'Tau': '\\Tau',
                     'Theta': '\\Theta',
                     'Upsilon': '\\Upsilon',
                     'Xi': '\\Xi',
                     'Zeta': '\\Zeta',
                     'alpha': '\\alpha',
                     'beta': '\\beta',
                     'chi': '\\chi',
                     'delta': '\\delta',
                     'epsilon': '\\epsilon',
                     'eta': '\\eta',
                     'gamma': '\\gamma',
                     'i': '\\varphi',
                     'iota': '\\iota',
                     'kappa': '\\kappa',
                     'lambda': '\\lambda',
                     'mu': '\\mu',
                     'nu': '\\nu',
                     'omega': '\\omega',
                     'phi': '\\phi',
                     'pi': '\\pi',
                     'psi': '\\psi',
                     'rho': '\\rho',
                     'sigma': '\\sigma',
                     'tau': '\\tau',
                     'theta': '\\theta',
                     'upsilon': '\\upsilon',
                     'varepsilon': '\\varepsilon',
                     'varkappa': '\\varkappa',
                     'varphi': '\\varphi',
                     'varpi': '\\varpi',
                     'varrho': '\\varrho',
                     'varsigma': '\\varsigma',
                     'vartheta': '\\vartheta',
                     'xi': '\\xi',
                     'zeta': '\\zeta'}
    functions = {'arccos': '\\arccos',
                 'arcsin': '\\arcsin',
                 'arctan': '\\arctan',
                 'cos': '\\cos',
                 'cosh': '\\cosh',
                 'cot': '\\cot',
                 'coth': '\\coth',
                 'csc': '\\csc',
                 'ln': '\\ln',
                 'log': '\\log',
                 'max': '\\max',
                 'min': '\\min',
                 'sec': '\\sec',
                 'sin': '\\sin',
                 'sinh': '\\sinh',
                 'tan': '\\tan',
                 'tanh': '\\tanh'}
    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:
            # parse know LaTeX functions
            if self.functions.has_key(func):
                return r'%s\left(%s\right)' % (self.functions[func], args)
            else:
                return r'\mbox{%s}\left(%s\right)' % (func, args)

    def prec_Call(self, n):
        return 1000

    def visit_Name(self, n):
        #parse greek letters
        name = n.id.split("_")
        for i in range(len(name)):
            if self.greekLetters.has_key(name[i]):
                name[i] = self.greekLetters[name[i]]
                
        #parse underscore and comma
        if len(name)>1:
            return name[0]+"_{"+','.join(name[1:])+"}"
        return name[0]

    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 '\\mbox{shiftLeft}'

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

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

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

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

    def visit_Invert(self, n):
        return '\\mbox{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):
        #walk ???
        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


get_ipython().register_magics(PrettyPrint)

I've modified some code I found on stackoverflow. I don't know if it can be legally included in the ipython distribution. However, I can have a workaround with regex and string functions. Had some code but it is more complicated than this.

identifiers for variables are parsed a bit different. The first occurrence of _ is the real LaTeX subscript. Next occurrences will give a comma.


Any comments?

Screenshot: http://i50.tinypic.com/2gvpwmv.png

Johan 		 	   		  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.scipy.org/pipermail/ipython-user/attachments/20120825/3cf11d53/attachment-0001.html 


More information about the IPython-User mailing list