From 27c3ba4a4d85322f19c0b3e1764d11db7a8706a1 Mon Sep 17 00:00:00 2001 From: Sarina Canelake Date: Mon, 10 Feb 2014 11:13:53 -0500 Subject: [PATCH] pep8/pylint cleanup on symmath/formula.py --- common/lib/symmath/symmath/formula.py | 402 ++++++++++++++------------ 1 file changed, 224 insertions(+), 178 deletions(-) diff --git a/common/lib/symmath/symmath/formula.py b/common/lib/symmath/symmath/formula.py index b7c4dca2b46d..5d25ad50520a 100644 --- a/common/lib/symmath/symmath/formula.py +++ b/common/lib/symmath/symmath/formula.py @@ -1,13 +1,15 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +""" +Flexible python representation of a symbolic mathematical formula. +Acceptes Presentation MathML, Content MathML (and could also do OpenMath). +Provides sympy representation. +""" # # File: formula.py # Date: 04-May-12 (creation) # Author: I. Chuang # -# flexible python representation of a symbolic mathematical formula. -# Acceptes Presentation MathML, Content MathML (and could also do OpenMath) -# Provides sympy representation. import os import string # pylint: disable=W0402 @@ -19,11 +21,8 @@ from sympy.printing.latex import LatexPrinter from sympy.printing.str import StrPrinter from sympy import latex, sympify -from sympy.physics.quantum.qubit import * -from sympy.physics.quantum.state import * -# from sympy import exp, pi, I -# from sympy.core.operations import LatticeOp -# import sympy.physics.quantum.qubit +from sympy.physics.quantum.qubit import Qubit +from sympy.physics.quantum.state import Ket from xml.sax.saxutils import unescape import unicodedata @@ -40,53 +39,63 @@ #----------------------------------------------------------------------------- -class dot(sympy.operations.LatticeOp): # my dot product +class dot(sympy.operations.LatticeOp): # pylint: disable=invalid-name, no-member + """my dot product""" zero = sympy.Symbol('dotzero') identity = sympy.Symbol('dotidentity') -#class dot(sympy.Mul): # my dot product -# is_Mul = False - -def _print_dot(self, expr): +def _print_dot(_self, expr): + """Print statement used for LatexPrinter""" return r'{((%s) \cdot (%s))}' % (expr.args[0], expr.args[1]) -LatexPrinter._print_dot = _print_dot +LatexPrinter._print_dot = _print_dot # pylint: disable=protected-access #----------------------------------------------------------------------------- # unit vectors (for 8.02) -def _print_hat(self, expr): return '\\hat{%s}' % str(expr.args[0]).lower() +def _print_hat(_self, expr): + """Print statement used for LatexPrinter""" + return '\\hat{%s}' % str(expr.args[0]).lower() -LatexPrinter._print_hat = _print_hat -StrPrinter._print_hat = _print_hat +LatexPrinter._print_hat = _print_hat # pylint: disable=protected-access +StrPrinter._print_hat = _print_hat # pylint: disable=protected-access #----------------------------------------------------------------------------- # helper routines -def to_latex(x): - if x is None: return '' - # LatexPrinter._print_dot = _print_dot - xs = latex(x) - xs = xs.replace(r'\XI', 'XI') # workaround for strange greek +def to_latex(expr): + """ + Convert expression to latex mathjax format + """ + if expr is None: + return '' + expr_s = latex(expr) + expr_s = expr_s.replace(r'\XI', 'XI') # workaround for strange greek # substitute back into latex form for scripts # literally something of the form # 'scriptN' becomes '\\mathcal{N}' # note: can't use something akin to the _print_hat method above because we sometimes get 'script(N)__B' or more complicated terms - xs = re.sub(r'script([a-zA-Z0-9]+)', - '\\mathcal{\\1}', - xs) + expr_s = re.sub( + r'script([a-zA-Z0-9]+)', + '\\mathcal{\\1}', + expr_s + ) #return '%s{}{}' % (xs[1:-1]) - if xs[0] == '$': - return '[mathjax]%s[/mathjax]
' % (xs[1:-1]) # for sympy v6 - return '[mathjax]%s[/mathjax]
' % (xs) # for sympy v7 + if expr_s[0] == '$': + return '[mathjax]%s[/mathjax]
' % (expr_s[1:-1]) # for sympy v6 + return '[mathjax]%s[/mathjax]
' % (expr_s) # for sympy v7 def my_evalf(expr, chop=False): + """ + Enhanced sympy evalf to handle lists of expressions + and catch eval failures without dropping out. + """ if type(expr) == list: try: return [x.evalf(chop=chop) for x in expr] @@ -97,11 +106,11 @@ def my_evalf(expr, chop=False): except: return expr -#----------------------------------------------------------------------------- -# my version of sympify to import expression into sympy - def my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False, symtab=None): + """ + Version of sympify to import expression into sympy + """ # make all lowercase real? if symtab: varset = symtab @@ -113,16 +122,13 @@ def my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False 'Q': sympy.Symbol('Q'), # otherwise it is a sympy "ask key" 'I': sympy.Symbol('I'), # otherwise it is sqrt(-1) 'N': sympy.Symbol('N'), # or it is some kind of sympy function - #'X':sympy.sympify('Matrix([[0,1],[1,0]])'), - #'Y':sympy.sympify('Matrix([[0,-I],[I,0]])'), - #'Z':sympy.sympify('Matrix([[1,0],[0,-1]])'), 'ZZ': sympy.Symbol('ZZ'), # otherwise it is the PythonIntegerRing 'XI': sympy.Symbol('XI'), # otherwise it is the capital \XI 'hat': sympy.Function('hat'), # for unit vectors (8.02) } if do_qubit: # turn qubit(...) into Qubit instance - varset.update({'qubit': sympy.physics.quantum.qubit.Qubit, - 'Ket': sympy.physics.quantum.state.Ket, + varset.update({'qubit': Qubit, + 'Ket': Ket, 'dot': dot, 'bit': sympy.Function('bit'), }) @@ -139,17 +145,21 @@ def my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False ophase = sympy.sympify('exp(-I*arg(%s))' % sexpr[0]) sexpr = [sympy.Mul(x, ophase) for x in sexpr] - def to_matrix(x): # if x is a list of lists, and is rectangular, then return Matrix(x) - if not type(x) == list: - return x - for row in x: + def to_matrix(expr): + """ + Convert a list, or list of lists to a matrix. + """ + # if expr is a list of lists, and is rectangular, then return Matrix(expr) + if not type(expr) == list: + return expr + for row in expr: if (not type(row) == list): - return x - rdim = len(x[0]) - for row in x: + return expr + rdim = len(expr[0]) + for row in expr: if not len(row) == rdim: - return x - return sympy.Matrix(x) + return expr + return sympy.Matrix(expr) if matrix: sexpr = to_matrix(sexpr) @@ -160,11 +170,11 @@ def to_matrix(x): # if x is a list of lists, and is rectangular, then return Ma class formula(object): - ''' + """ Representation of a mathematical formula object. Accepts mathml math expression for constructing, and can produce sympy translation. The formula may or may not include an assignment (=). - ''' + """ def __init__(self, expr, asciimath='', options=None): self.expr = expr.strip() self.asciimath = asciimath @@ -173,14 +183,23 @@ def __init__(self, expr, asciimath='', options=None): self.options = options def is_presentation_mathml(self): + """ + Check if formula is in mathml presentation format. + """ return 'f-2" - # this is really terrible for turning into cmathml. - # undo this here. def fix_pmathml(xml): + """ + f and g are processed as functions by asciimathml, eg "f-2" turns + into "f-2" this is + really terrible for turning into cmathml. undo this here. + """ for k in xml: tag = gettag(k) if tag == 'mrow': @@ -235,10 +257,13 @@ def fix_pmathml(xml): fix_pmathml(xml) - # hat i is turned into i^ ; mangle this into hat(f) - # hat i also somtimes turned into j ^ def fix_hat(xml): + """ + hat i is turned into i^ ; mangle + this into hat(f) hat i also somtimes turned into + j ^ + """ for k in xml: tag = gettag(k) if tag == 'mover': @@ -255,7 +280,8 @@ def fix_hat(xml): fix_hat(xml) def flatten_pmathml(xml): - ''' Give the text version of certain PMathML elements + """ + Give the text version of certain PMathML elements Sometimes MathML will be given with each letter separated (it doesn't know if its implicit multiplication or what). From an xml @@ -266,19 +292,23 @@ def flatten_pmathml(xml): x and returns 'max', for easier use later on. - ''' + """ tag = gettag(xml) - if tag == 'mn': return xml.text - elif tag == 'mi': return xml.text - elif tag == 'mrow': return ''.join([flatten_pmathml(y) for y in xml]) - raise Exception, '[flatten_pmathml] unknown tag %s' % tag + if tag == 'mn': + return xml.text + elif tag == 'mi': + return xml.text + elif tag == 'mrow': + return ''.join([flatten_pmathml(y) for y in xml]) + raise Exception('[flatten_pmathml] unknown tag %s' % tag) def fix_mathvariant(parent): - '''Fix certain kinds of math variants + """ + Fix certain kinds of math variants Literally replace N with 'scriptN'. There have been problems using script_N or script(N) - ''' + """ for child in parent: if (gettag(child) == 'mstyle' and child.get('mathvariant') == 'script'): newchild = etree.Element('mi') @@ -287,12 +317,11 @@ def fix_mathvariant(parent): fix_mathvariant(child) fix_mathvariant(xml) - # find "tagged" superscripts # they have the character \u200b in the superscript # replace them with a__b so snuggle doesn't get confused def fix_superscripts(xml): - ''' Look for and replace sup elements with 'X__Y' or 'X_Y__Z' + """ Look for and replace sup elements with 'X__Y' or 'X_Y__Z' In the javascript, variables with '__X' in them had an invisible character inserted into the sup (to distinguish from powers) @@ -324,16 +353,18 @@ def fix_superscripts(xml): to be 'x__B' - ''' + """ for k in xml: tag = gettag(k) # match things like the last example-- # the second item in msub is an mrow with the first # character equal to \u200b - if (tag == 'msup' and - len(k) == 2 and gettag(k[1]) == 'mrow' and - gettag(k[1][0]) == 'mo' and k[1][0].text == u'\u200b'): # whew + if ( + tag == 'msup' and + len(k) == 2 and gettag(k[1]) == 'mrow' and + gettag(k[1][0]) == 'mo' and k[1][0].text == u'\u200b' # whew + ): # replace the msup with 'X__Y' k[1].remove(k[1][0]) @@ -344,9 +375,11 @@ def fix_superscripts(xml): # match things like the middle example- # the third item in msubsup is an mrow with the first # character equal to \u200b - if (tag == 'msubsup' and - len(k) == 3 and gettag(k[2]) == 'mrow' and - gettag(k[2][0]) == 'mo' and k[2][0].text == u'\u200b'): # whew + if ( + tag == 'msubsup' and + len(k) == 3 and gettag(k[2]) == 'mrow' and + gettag(k[2][0]) == 'mo' and k[2][0].text == u'\u200b' # whew + ): # replace the msubsup with 'X_Y__Z' k[2].remove(k[2][0]) @@ -357,10 +390,12 @@ def fix_superscripts(xml): fix_superscripts(k) fix_superscripts(xml) - # Snuggle returns an error when it sees an - # replace such elements with an , except the first element is of - # the form a_b. I.e. map a_b^c => (a_b)^c def fix_msubsup(parent): + """ + Snuggle returns an error when it sees an replace such + elements with an , except the first element is of + the form a_b. I.e. map a_b^c => (a_b)^c + """ for child in parent: # fix msubsup if (gettag(child) == 'msubsup' and len(child) == 3): @@ -375,20 +410,21 @@ def fix_msubsup(parent): fix_msubsup(child) fix_msubsup(xml) - self.xml = xml + self.xml = xml # pylint: disable=attribute-defined-outside-init return self.xml def get_content_mathml(self): - if self.the_cmathml: return self.the_cmathml + if self.the_cmathml: + return self.the_cmathml # pre-process the presentation mathml before sending it to snuggletex to convert to content mathml try: xml = self.preprocess_pmathml(self.expr) except Exception, err: - log.warning('Err %s while preprocessing; expr=%s' % (err, self.expr)) + log.warning('Err %s while preprocessing; expr=%s', err, self.expr) return "Error! Cannot process pmathml" pmathml = etree.tostring(xml, pretty_print=True) - self.the_pmathml = pmathml + self.the_pmathml = pmathml # pylint: disable=attribute-defined-outside-init # convert to cmathml self.the_cmathml = self.GetContentMathML(self.asciimath, pmathml) @@ -397,15 +433,16 @@ def get_content_mathml(self): cmathml = property(get_content_mathml, None, None, 'content MathML representation') def make_sympy(self, xml=None): - ''' + """ Return sympy expression for the math formula. The math formula is converted to Content MathML then that is parsed. This is a recursive function, called on every CMML node. Support for more functions can be added by modifying opdict, abould halfway down - ''' + """ - if self.the_sympy: return self.the_sympy + if self.the_sympy: + return self.the_sympy if xml is None: # root if not self.is_mathml(): @@ -420,7 +457,7 @@ def make_sympy(self, xml=None): msg = "Illegal math expression" else: msg = 'Err %s while converting cmathml to xml; cmml=%s' % (err, cmml) - raise Exception, msg + raise Exception(msg) xml = self.fix_greek_in_mathml(xml) self.the_sympy = self.make_sympy(xml[0]) else: @@ -429,113 +466,116 @@ def make_sympy(self, xml=None): self.the_sympy = self.make_sympy(xml[0]) return self.the_sympy - def gettag(x): - return re.sub('{http://[^}]+}', '', x.tag) + def gettag(expr): + return re.sub('{http://[^}]+}', '', expr.tag) # simple math def op_divide(*args): if not len(args) == 2: - raise Exception, 'divide given wrong number of arguments!' + raise Exception('divide given wrong number of arguments!') # print "divide: arg0=%s, arg1=%s" % (args[0],args[1]) return sympy.Mul(args[0], sympy.Pow(args[1], -1)) - def op_plus(*args): return args[0] if len(args) == 1 else op_plus(*args[:-1]) + args[-1] + def op_plus(*args): + return args[0] if len(args) == 1 else op_plus(*args[:-1]) + args[-1] - def op_times(*args): return reduce(operator.mul, args) + def op_times(*args): + return reduce(operator.mul, args) def op_minus(*args): if len(args) == 1: return -args[0] if not len(args) == 2: - raise Exception, 'minus given wrong number of arguments!' + raise Exception('minus given wrong number of arguments!') #return sympy.Add(args[0],-args[1]) return args[0] - args[1] - opdict = {'plus': op_plus, - 'divide': operator.div, - 'times': op_times, - 'minus': op_minus, - #'plus': sympy.Add, - #'divide' : op_divide, - #'times' : sympy.Mul, - 'minus': op_minus, - 'root': sympy.sqrt, - 'power': sympy.Pow, - 'sin': sympy.sin, - 'cos': sympy.cos, - 'tan': sympy.tan, - 'cot': sympy.cot, - 'sinh': sympy.sinh, - 'cosh': sympy.cosh, - 'coth': sympy.coth, - 'tanh': sympy.tanh, - 'asin': sympy.asin, - 'acos': sympy.acos, - 'atan': sympy.atan, - 'atan2': sympy.atan2, - 'acot': sympy.acot, - 'asinh': sympy.asinh, - 'acosh': sympy.acosh, - 'atanh': sympy.atanh, - 'acoth': sympy.acoth, - 'exp': sympy.exp, - 'log': sympy.log, - 'ln': sympy.ln, - } - - # simple sumbols - nums1dict = {'pi': sympy.pi, - } + opdict = { + 'plus': op_plus, + 'divide': operator.div, # should this be op_divide? + 'times': op_times, + 'minus': op_minus, + 'root': sympy.sqrt, + 'power': sympy.Pow, + 'sin': sympy.sin, + 'cos': sympy.cos, + 'tan': sympy.tan, + 'cot': sympy.cot, + 'sinh': sympy.sinh, + 'cosh': sympy.cosh, + 'coth': sympy.coth, + 'tanh': sympy.tanh, + 'asin': sympy.asin, + 'acos': sympy.acos, + 'atan': sympy.atan, + 'atan2': sympy.atan2, + 'acot': sympy.acot, + 'asinh': sympy.asinh, + 'acosh': sympy.acosh, + 'atanh': sympy.atanh, + 'acoth': sympy.acoth, + 'exp': sympy.exp, + 'log': sympy.log, + 'ln': sympy.ln, + } + + # simple symbols - TODO is this code used? + nums1dict = { + 'pi': sympy.pi, + } def parsePresentationMathMLSymbol(xml): - ''' + """ Parse , , , and - ''' + """ tag = gettag(xml) - if tag == 'mn': return xml.text - elif tag == 'mi': return xml.text - elif tag == 'msub': return '_'.join([parsePresentationMathMLSymbol(y) for y in xml]) - elif tag == 'msup': return '^'.join([parsePresentationMathMLSymbol(y) for y in xml]) - raise Exception, '[parsePresentationMathMLSymbol] unknown tag %s' % tag + if tag == 'mn': + return xml.text + elif tag == 'mi': + return xml.text + elif tag == 'msub': + return '_'.join([parsePresentationMathMLSymbol(y) for y in xml]) + elif tag == 'msup': + return '^'.join([parsePresentationMathMLSymbol(y) for y in xml]) + raise Exception('[parsePresentationMathMLSymbol] unknown tag %s' % tag) # parser tree for Content MathML tag = gettag(xml) - # print "tag = ",tag # first do compound objects if tag == 'apply': # apply operator opstr = gettag(xml[0]) if opstr in opdict: - op = opdict[opstr] - args = [self.make_sympy(x) for x in xml[1:]] + op = opdict[opstr] # pylint: disable=invalid-name + args = [self.make_sympy(expr) for expr in xml[1:]] try: res = op(*args) except Exception, err: - self.args = args - self.op = op - raise Exception, '[formula] error=%s failed to apply %s to args=%s' % (err, opstr, args) + self.args = args # pylint: disable=attribute-defined-outside-init + self.op = op # pylint: disable=attribute-defined-outside-init, invalid-name + raise Exception('[formula] error=%s failed to apply %s to args=%s' % (err, opstr, args)) return res else: - raise Exception, '[formula]: unknown operator tag %s' % (opstr) + raise Exception('[formula]: unknown operator tag %s' % (opstr)) elif tag == 'list': # square bracket list if gettag(xml[0]) == 'matrix': return self.make_sympy(xml[0]) else: - return [self.make_sympy(x) for x in xml] + return [self.make_sympy(expr) for expr in xml] elif tag == 'matrix': - return sympy.Matrix([self.make_sympy(x) for x in xml]) + return sympy.Matrix([self.make_sympy(expr) for expr in xml]) elif tag == 'vector': - return [self.make_sympy(x) for x in xml] + return [self.make_sympy(expr) for expr in xml] # atoms are below elif tag == 'cn': # number return sympy.sympify(xml.text) - return float(xml.text) + # return float(xml.text) elif tag == 'ci': # variable (symbol) if len(xml) > 0 and (gettag(xml[0]) == 'msub' or gettag(xml[0]) == 'msup'): # subscript or superscript @@ -553,27 +593,29 @@ def parsePresentationMathMLSymbol(xml): return sym else: # unknown tag - raise Exception, '[formula] unknown tag %s' % tag + raise Exception('[formula] unknown tag %s' % tag) sympy = property(make_sympy, None, None, 'sympy representation') def GetContentMathML(self, asciimath, mathml): - # URL = 'http://192.168.1.2:8080/snuggletex-webapp-1.2.2/ASCIIMathMLUpConversionDemo' - # URL = 'http://127.0.0.1:8080/snuggletex-webapp-1.2.2/ASCIIMathMLUpConversionDemo' - URL = 'https://math-xserver.mitx.mit.edu/snuggletex-webapp-1.2.2/ASCIIMathMLUpConversionDemo' + """ + Handle requests to snuggletex API to convert the Ascii math to MathML + """ + # url = 'http://192.168.1.2:8080/snuggletex-webapp-1.2.2/ASCIIMathMLUpConversionDemo' + # url = 'http://127.0.0.1:8080/snuggletex-webapp-1.2.2/ASCIIMathMLUpConversionDemo' + url = 'https://math-xserver.mitx.mit.edu/snuggletex-webapp-1.2.2/ASCIIMathMLUpConversionDemo' if 1: - payload = {'asciiMathInput': asciimath, - 'asciiMathML': mathml, - #'asciiMathML':unicode(mathml).encode('utf-8'), - } + payload = { + 'asciiMathInput': asciimath, + 'asciiMathML': mathml, + #'asciiMathML':unicode(mathml).encode('utf-8'), + } headers = {'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13"} - r = requests.post(URL, data=payload, headers=headers, verify=False) - r.encoding = 'utf-8' - ret = r.text - #print "encoding: ",r.encoding - - # return ret + request = requests.post(url, data=payload, headers=headers, verify=False) + request.encoding = 'utf-8' + ret = request.text + # print "encoding: ", request.encoding mode = 0 cmathml = [] @@ -586,18 +628,17 @@ def GetContentMathML(self, asciimath, mathml): mode = 0 continue cmathml.append(k) - # return '\n'.join(cmathml) cmathml = '\n'.join(cmathml[2:]) cmathml = '\n' + unescape(cmathml) + '\n' # print cmathml - #return unicode(cmathml) return cmathml #----------------------------------------------------------------------------- def test1(): - xmlstr = ''' + """Test XML strings - addition""" + xmlstr = """ @@ -605,12 +646,13 @@ def test1(): 2 - ''' + """ return formula(xmlstr) def test2(): - xmlstr = u''' + """Test XML strings - addition, Greek alpha""" + xmlstr = u""" @@ -622,12 +664,13 @@ def test2(): - ''' + """ return formula(xmlstr) def test3(): - xmlstr = ''' + """Test XML strings - addition, Greek gamma""" + xmlstr = """ @@ -639,12 +682,13 @@ def test3(): - ''' + """ return formula(xmlstr) def test4(): - xmlstr = u''' + """Test XML strings - addition, Greek alpha, mfrac""" + xmlstr = u""" 1 @@ -655,12 +699,13 @@ def test4(): -''' +""" return formula(xmlstr) -def test5(): # sum of two matrices - xmlstr = u''' +def test5(): + """Test XML strings - sum of two matrices""" + xmlstr = u""" @@ -719,12 +764,13 @@ def test5(): # sum of two matrices -''' +""" return formula(xmlstr) -def test6(): # imaginary numbers - xmlstr = u''' +def test6(): + """Test XML strings - imaginary numbers""" + xmlstr = u""" 1 @@ -732,5 +778,5 @@ def test6(): # imaginary numbers i -''' +""" return formula(xmlstr, options='imaginary')