Merge pull request #2538 from edx/sarina/fixup-symmath
pep8/pylint cleanup on symmath/formula.py
This commit is contained in:
@@ -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 <ichuang@mit.edu>
|
||||
#
|
||||
# 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 @@ import sympy
|
||||
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 @@ os.environ['PYTHONIOENCODING'] = 'utf-8'
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
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 '<math>%s{}{}</math>' % (xs[1:-1])
|
||||
if xs[0] == '$':
|
||||
return '[mathjax]%s[/mathjax]<br>' % (xs[1:-1]) # for sympy v6
|
||||
return '[mathjax]%s[/mathjax]<br>' % (xs) # for sympy v7
|
||||
if expr_s[0] == '$':
|
||||
return '[mathjax]%s[/mathjax]<br>' % (expr_s[1:-1]) # for sympy v6
|
||||
return '[mathjax]%s[/mathjax]<br>' % (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 my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False
|
||||
|
||||
|
||||
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 @@ class formula(object):
|
||||
self.options = options
|
||||
|
||||
def is_presentation_mathml(self):
|
||||
"""
|
||||
Check if formula is in mathml presentation format.
|
||||
"""
|
||||
return '<mstyle' in self.expr
|
||||
|
||||
def is_mathml(self):
|
||||
"""
|
||||
Check if formula is in mathml format.
|
||||
"""
|
||||
return '<math ' in self.expr
|
||||
|
||||
def fix_greek_in_mathml(self, xml):
|
||||
def gettag(x):
|
||||
return re.sub('{http://[^}]+}', '', x.tag)
|
||||
"""
|
||||
Recursively fix greek letters in passed in xml.
|
||||
"""
|
||||
def gettag(expr):
|
||||
return re.sub('{http://[^}]+}', '', expr.tag)
|
||||
|
||||
for k in xml:
|
||||
tag = gettag(k)
|
||||
@@ -188,40 +207,43 @@ class formula(object):
|
||||
usym = unicode(k.text)
|
||||
try:
|
||||
udata = unicodedata.name(usym)
|
||||
except Exception, err:
|
||||
except Exception:
|
||||
udata = None
|
||||
#print "usym = %s, udata=%s" % (usym,udata)
|
||||
# print "usym = %s, udata=%s" % (usym,udata)
|
||||
if udata: # eg "GREEK SMALL LETTER BETA"
|
||||
if 'GREEK' in udata:
|
||||
usym = udata.split(' ')[-1]
|
||||
if 'SMALL' in udata: usym = usym.lower()
|
||||
if 'SMALL' in udata:
|
||||
usym = usym.lower()
|
||||
#print "greek: ",usym
|
||||
k.text = usym
|
||||
self.fix_greek_in_mathml(k)
|
||||
return xml
|
||||
|
||||
def preprocess_pmathml(self, xml):
|
||||
r'''
|
||||
r"""
|
||||
Pre-process presentation MathML from ASCIIMathML to make it more
|
||||
acceptable for SnuggleTeX, and also to accomodate some sympy
|
||||
conventions (eg hat(i) for \hat{i}).
|
||||
|
||||
This method would be a good spot to look for an integral and convert
|
||||
it, if possible...
|
||||
'''
|
||||
"""
|
||||
|
||||
if type(xml) == str or type(xml) == unicode:
|
||||
xml = etree.fromstring(xml) # TODO: wrap in try
|
||||
|
||||
xml = self.fix_greek_in_mathml(xml) # convert greek utf letters to greek spelled out in ascii
|
||||
|
||||
def gettag(x):
|
||||
return re.sub('{http://[^}]+}', '', x.tag)
|
||||
def gettag(expr):
|
||||
return re.sub('{http://[^}]+}', '', expr.tag)
|
||||
|
||||
# f and g are processed as functions by asciimathml, eg "f-2" turns into "<mrow><mi>f</mi><mo>-</mo></mrow><mn>2</mn>"
|
||||
# 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 "<mrow><mi>f</mi><mo>-</mo></mrow><mn>2</mn>" 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 @@ class formula(object):
|
||||
|
||||
fix_pmathml(xml)
|
||||
|
||||
# hat i is turned into <mover><mi>i</mi><mo>^</mo></mover> ; mangle this into <mi>hat(f)</mi>
|
||||
# hat i also somtimes turned into <mover><mrow> <mi>j</mi> </mrow><mo>^</mo></mover>
|
||||
|
||||
def fix_hat(xml):
|
||||
"""
|
||||
hat i is turned into <mover><mi>i</mi><mo>^</mo></mover> ; mangle
|
||||
this into <mi>hat(f)</mi> hat i also somtimes turned into
|
||||
<mover><mrow> <mi>j</mi> </mrow><mo>^</mo></mover>
|
||||
"""
|
||||
for k in xml:
|
||||
tag = gettag(k)
|
||||
if tag == 'mover':
|
||||
@@ -255,7 +280,8 @@ class formula(object):
|
||||
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 @@ class formula(object):
|
||||
<mi>x</mi>
|
||||
</mrow>
|
||||
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 <mstyle mathvariant="script"><mi>N</mi></mstyle>
|
||||
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 @@ class formula(object):
|
||||
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 @@ class formula(object):
|
||||
</mrow>
|
||||
</msup>
|
||||
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 @@ class formula(object):
|
||||
# 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 @@ class formula(object):
|
||||
fix_superscripts(k)
|
||||
fix_superscripts(xml)
|
||||
|
||||
# Snuggle returns an error when it sees an <msubsup>
|
||||
# replace such elements with an <msup>, 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 <msubsup> replace such
|
||||
elements with an <msup>, 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 @@ class formula(object):
|
||||
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 "<html>Error! Cannot process pmathml</html>"
|
||||
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 @@ class formula(object):
|
||||
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 @@ class formula(object):
|
||||
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 @@ class formula(object):
|
||||
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,
|
||||
}
|
||||
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 sumbols
|
||||
nums1dict = {'pi': sympy.pi,
|
||||
}
|
||||
# simple symbols - TODO is this code used?
|
||||
nums1dict = {
|
||||
'pi': sympy.pi,
|
||||
}
|
||||
|
||||
def parsePresentationMathMLSymbol(xml):
|
||||
'''
|
||||
"""
|
||||
Parse <msub>, <msup>, <mi>, and <mn>
|
||||
'''
|
||||
"""
|
||||
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 @@ class formula(object):
|
||||
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 @@ class formula(object):
|
||||
mode = 0
|
||||
continue
|
||||
cmathml.append(k)
|
||||
# return '\n'.join(cmathml)
|
||||
cmathml = '\n'.join(cmathml[2:])
|
||||
cmathml = '<math xmlns="http://www.w3.org/1998/Math/MathML">\n' + unescape(cmathml) + '\n</math>'
|
||||
# print cmathml
|
||||
#return unicode(cmathml)
|
||||
return cmathml
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test1():
|
||||
xmlstr = '''
|
||||
"""Test XML strings - addition"""
|
||||
xmlstr = """
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<apply>
|
||||
<plus/>
|
||||
@@ -605,12 +646,13 @@ def test1():
|
||||
<cn>2</cn>
|
||||
</apply>
|
||||
</math>
|
||||
'''
|
||||
"""
|
||||
return formula(xmlstr)
|
||||
|
||||
|
||||
def test2():
|
||||
xmlstr = u'''
|
||||
"""Test XML strings - addition, Greek alpha"""
|
||||
xmlstr = u"""
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<apply>
|
||||
<plus/>
|
||||
@@ -622,12 +664,13 @@ def test2():
|
||||
</apply>
|
||||
</apply>
|
||||
</math>
|
||||
'''
|
||||
"""
|
||||
return formula(xmlstr)
|
||||
|
||||
|
||||
def test3():
|
||||
xmlstr = '''
|
||||
"""Test XML strings - addition, Greek gamma"""
|
||||
xmlstr = """
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<apply>
|
||||
<divide/>
|
||||
@@ -639,12 +682,13 @@ def test3():
|
||||
</apply>
|
||||
</apply>
|
||||
</math>
|
||||
'''
|
||||
"""
|
||||
return formula(xmlstr)
|
||||
|
||||
|
||||
def test4():
|
||||
xmlstr = u'''
|
||||
"""Test XML strings - addition, Greek alpha, mfrac"""
|
||||
xmlstr = u"""
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<mstyle displaystyle="true">
|
||||
<mn>1</mn>
|
||||
@@ -655,12 +699,13 @@ def test4():
|
||||
</mfrac>
|
||||
</mstyle>
|
||||
</math>
|
||||
'''
|
||||
"""
|
||||
return formula(xmlstr)
|
||||
|
||||
|
||||
def test5(): # sum of two matrices
|
||||
xmlstr = u'''
|
||||
def test5():
|
||||
"""Test XML strings - sum of two matrices"""
|
||||
xmlstr = u"""
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<mstyle displaystyle="true">
|
||||
<mrow>
|
||||
@@ -719,12 +764,13 @@ def test5(): # sum of two matrices
|
||||
</mrow>
|
||||
</mstyle>
|
||||
</math>
|
||||
'''
|
||||
"""
|
||||
return formula(xmlstr)
|
||||
|
||||
|
||||
def test6(): # imaginary numbers
|
||||
xmlstr = u'''
|
||||
def test6():
|
||||
"""Test XML strings - imaginary numbers"""
|
||||
xmlstr = u"""
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<mstyle displaystyle="true">
|
||||
<mn>1</mn>
|
||||
@@ -732,5 +778,5 @@ def test6(): # imaginary numbers
|
||||
<mi>i</mi>
|
||||
</mstyle>
|
||||
</math>
|
||||
'''
|
||||
"""
|
||||
return formula(xmlstr, options='imaginary')
|
||||
|
||||
Reference in New Issue
Block a user