From 672ede14d196b9128c2ad13fcbcff24911ad86e9 Mon Sep 17 00:00:00 2001
From: ichuang
Date: Wed, 19 Sep 2012 20:37:26 -0400
Subject: [PATCH 1/3] improvements in symbolic math checking - more standard
functions, use new capa_alert div for error messages, clean up debugging
---
lms/lib/symmath/formula.py | 26 +++++++++++++++++++++++++-
lms/lib/symmath/symmath_check.py | 32 +++++++++++++++++++++++---------
2 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/lms/lib/symmath/formula.py b/lms/lib/symmath/formula.py
index 4aa9f60d30..c71a79bac7 100644
--- a/lms/lib/symmath/formula.py
+++ b/lms/lib/symmath/formula.py
@@ -105,6 +105,7 @@ def my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False
'e': sympy.E, # for exp
'i': sympy.I, # lowercase i is also sqrt(-1)
'Q': sympy.Symbol('Q'), # otherwise it is a sympy "ask key"
+ 'I': sympy.Symbol('I'), # otherwise it is sqrt(-1)
#'X':sympy.sympify('Matrix([[0,1],[1,0]])'),
#'Y':sympy.sympify('Matrix([[0,-I],[I,0]])'),
#'Z':sympy.sympify('Matrix([[1,0],[0,-1]])'),
@@ -273,11 +274,16 @@ class formula(object):
if not self.is_mathml():
return my_sympify(self.expr)
if self.is_presentation_mathml():
+ cmml = None
try:
cmml = self.cmathml
xml = etree.fromstring(str(cmml))
except Exception, err:
- raise Exception, 'Err %s while converting cmathml to xml; cmml=%s' % (err, cmml)
+ if 'conversion from Presentation MathML to Content MathML was not successful' in cmml:
+ msg = "Illegal math expression"
+ else:
+ msg = 'Err %s while converting cmathml to xml; cmml=%s' % (err, cmml)
+ raise Exception, msg
xml = self.fix_greek_in_mathml(xml)
self.the_sympy = self.make_sympy(xml[0])
else:
@@ -320,6 +326,24 @@ class formula(object):
'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
diff --git a/lms/lib/symmath/symmath_check.py b/lms/lib/symmath/symmath_check.py
index 4af012d2cb..09810b8820 100644
--- a/lms/lib/symmath/symmath_check.py
+++ b/lms/lib/symmath/symmath_check.py
@@ -140,13 +140,20 @@ def check(expect, given, numerical=False, matrix=False, normphase=False, abcsym=
# msg += " dot test " + to_latex(dot(sympy.Symbol('x'),sympy.Symbol('y')))
return {'ok': False, 'msg': msg}
+#-----------------------------------------------------------------------------
+# helper function to convert all to
+
+def make_error_message(msg):
+ # msg = msg.replace('','
').replace('
','
')
+ msg = '%s
' % msg
+ return msg
+
#-----------------------------------------------------------------------------
# Check function interface, which takes pmathml input
#
# This is one of the main entry points to call.
-
-def symmath_check(expect, ans, dynamath=None, options=None, debug=None):
+def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None):
'''
Check a symbolic mathematical expression using sympy.
The input may be presentation MathML. Uses formula.
@@ -159,6 +166,11 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None):
threshold = 1.0e-3
DEBUG = debug
+ if xml is not None:
+ DEBUG = xml.get('debug',False) # override debug flag using attribute in symbolicmath xml
+ if DEBUG in ['0','False']:
+ DEBUG = False
+
# options
do_matrix = 'matrix' in (options or '')
do_qubit = 'qubit' in (options or '')
@@ -169,7 +181,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None):
fexpect = my_sympify(str(expect), matrix=do_matrix, do_qubit=do_qubit)
except Exception, err:
msg += 'Error %s in parsing OUR expected answer "%s"
' % (err, expect)
- return {'ok': False, 'msg': msg}
+ return {'ok': False, 'msg': make_error_message(msg)}
# if expected answer is a number, try parsing provided answer as a number also
try:
@@ -205,16 +217,18 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None):
msg += 'You entered: %s
' % to_latex(f.sympy)
except Exception, err:
log.exception("Error evaluating expression '%s' as a valid equation" % ans)
- msg += "Error %s in evaluating your expression '%s' as a valid equation
" % (str(err).replace('<', '<'),
- ans)
+ msg += "Error in evaluating your expression '%s' as a valid equation
" % (ans)
+ if "Illegal math" in str(err):
+ msg += "Illegal math expression
"
if DEBUG:
+ msg += 'Error: %s' % str(err).replace('<', '<')
msg += '
'
msg += 'DEBUG messages:
'
msg += "%s
" % traceback.format_exc()
msg += 'cmathml=
%s
' % f.cmathml.replace('<', '<')
msg += 'pmathml=
%s
' % mmlans.replace('<', '<')
msg += '
'
- return {'ok': False, 'msg': msg}
+ return {'ok': False, 'msg': make_error_message(msg)}
# compare with expected
if hasattr(fexpect, 'is_number') and fexpect.is_number:
@@ -226,7 +240,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None):
msg += "given = %s
" % repr(ans)
msg += "fsym = %s
" % repr(fsym)
# msg += "cmathml =
%s
" % str(f.cmathml).replace('<','<')
- return {'ok': False, 'msg': msg}
+ return {'ok': False, 'msg': make_error_message(msg)}
if fexpect == fsym:
return {'ok': True, 'msg': msg}
@@ -239,11 +253,11 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None):
return {'ok': True, 'msg': msg}
except sympy.ShapeError:
msg += "Error - your input vector or matrix has the wrong dimensions"
- return {'ok': False, 'msg': msg}
+ return {'ok': False, 'msg': make_error_message(msg)}
except Exception, err:
msg += "
Error %s in comparing expected (a list) and your answer
" % str(err).replace('<', '<')
if DEBUG: msg += "%s
" % traceback.format_exc()
- return {'ok': False, 'msg': msg}
+ return {'ok': False, 'msg': make_error_message(msg)}
#diff = (fexpect-fsym).simplify()
#fsym = fsym.simplify()
From f1e5d6976ef5c1f680b7e6822a9b72bb3545df46 Mon Sep 17 00:00:00 2001
From: ichuang
Date: Wed, 19 Sep 2012 20:49:36 -0400
Subject: [PATCH 2/3] fix tabs fallback check when no tabs specified
---
lms/djangoapps/courseware/tabs.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lms/djangoapps/courseware/tabs.py b/lms/djangoapps/courseware/tabs.py
index 8ecd8137df..cad70980e2 100644
--- a/lms/djangoapps/courseware/tabs.py
+++ b/lms/djangoapps/courseware/tabs.py
@@ -166,7 +166,7 @@ def get_course_tabs(user, course, active_page):
"""
Return the tabs to show a particular user, as a list of CourseTab items.
"""
- if not course.tabs:
+ if not hasattr(course,'tabs') or not course.tabs:
return get_default_tabs(user, course, active_page)
# TODO (vshnayder): There needs to be a place to call this right after course
From b9af428b40309c42abba0c71f5552cbb1b1eed3a Mon Sep 17 00:00:00 2001
From: ichuang
Date: Thu, 20 Sep 2012 14:37:21 -0400
Subject: [PATCH 3/3] formula.py handle imaginary correctly; symmath_check add
numerical option
---
lms/lib/symmath/formula.py | 3 +--
lms/lib/symmath/symmath_check.py | 8 ++++++++
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/lms/lib/symmath/formula.py b/lms/lib/symmath/formula.py
index c71a79bac7..1698b004d9 100644
--- a/lms/lib/symmath/formula.py
+++ b/lms/lib/symmath/formula.py
@@ -409,8 +409,7 @@ class formula(object):
if 'hat' in usym:
sym = my_sympify(usym)
else:
- if usym == 'i': print "options=", self.options
- if usym == 'i' and 'imaginary' in self.options: # i = sqrt(-1)
+ if usym == 'i' and self.options is not None and 'imaginary' in self.options: # i = sqrt(-1)
sym = sympy.I
else:
sym = sympy.Symbol(str(usym))
diff --git a/lms/lib/symmath/symmath_check.py b/lms/lib/symmath/symmath_check.py
index 09810b8820..bcb4a0d490 100644
--- a/lms/lib/symmath/symmath_check.py
+++ b/lms/lib/symmath/symmath_check.py
@@ -175,6 +175,7 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
do_matrix = 'matrix' in (options or '')
do_qubit = 'qubit' in (options or '')
do_imaginary = 'imaginary' in (options or '')
+ do_numerical = 'numerical' in (options or '')
# parse expected answer
try:
@@ -196,6 +197,13 @@ def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None
msg += 'You entered: %s
' % to_latex(fans)
return {'ok': False, 'msg': msg}
+ if do_numerical: # numerical answer expected - force numerical comparison
+ if abs(abs(fans - fexpect) / fexpect) < threshold:
+ return {'ok': True, 'msg': msg}
+ else:
+ msg += 'You entered: %s (note that a numerical answer is expected)
' % to_latex(fans)
+ return {'ok': False, 'msg': msg}
+
if fexpect == fans:
msg += 'You entered: %s
' % to_latex(fans)
return {'ok': True, 'msg': msg}