From 6304feefd649cd7b52d1830dd9a33551fca02414 Mon Sep 17 00:00:00 2001 From: Piotr Mitros Date: Fri, 3 Feb 2012 18:48:44 -0500 Subject: [PATCH] Calculator changes for Anant --- courseware/capa/calc.py | 40 ++++++++++++++++++++++++++++++++-------- util/views.py | 2 +- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/courseware/capa/calc.py b/courseware/capa/calc.py index bd4136d393..41ced03e58 100644 --- a/courseware/capa/calc.py +++ b/courseware/capa/calc.py @@ -1,17 +1,41 @@ +import copy import math import operator +import numpy + from pyparsing import Word, alphas, nums, oneOf, Literal from pyparsing import ZeroOrMore, OneOrMore, StringStart from pyparsing import StringEnd, Optional, Forward from pyparsing import CaselessLiteral, Group, StringEnd from pyparsing import NoMatch, stringEnd +default_functions = {'sin' : numpy.sin, + 'cos' : numpy.cos, + 'tan' : numpy.tan, + 'sqrt': numpy.sqrt, + 'log10':numpy.log10, + 'log2':numpy.log2, + 'ln': numpy.log, + 'arccos':numpy.arccos, + 'arcsin':numpy.arcsin, + 'arctan':numpy.arctan, + 'abs':numpy.abs + } +default_variables = {'j':numpy.complex(0,1), + 'e':numpy.complex(numpy.e) + } + def evaluator(variables, functions, string): ''' Evaluate an expression. Variables are passed as a dictionary from string to value. Unary functions are passed as a dictionary from string to function ''' + all_variables = copy.copy(default_variables) + all_variables.update(variables) + all_functions = copy.copy(default_functions) + all_functions.update(functions) + if string.strip() == "": return float('nan') ops = { "^" : operator.pow, @@ -38,7 +62,7 @@ def evaluator(variables, functions, string): def number_parse_action(x): # [ '7' ] -> [ 7 ] return [super_float("".join(x))] def exp_parse_action(x): # [ 2 ^ 3 ^ 2 ] -> 512 - x = [e for e in x if type(e) == float] # Ignore ^ + x = [e for e in x if type(e) in [float, numpy.float64, numpy.complex]] # Ignore ^ x.reverse() x=reduce(lambda a,b:b**a, x) return x @@ -68,7 +92,7 @@ def evaluator(variables, functions, string): prod=op(prod, e) return prod def func_parse_action(x): - return [functions[x[0]](x[1])] + return [all_functions[x[0]](x[1])] number_suffix=reduce(lambda a,b:a|b, map(Literal,suffixes.keys()), NoMatch()) # SI suffixes and percent (dot,minus,plus,times,div,lpar,rpar,exp)=map(Literal,".-+*/()^") @@ -94,14 +118,14 @@ def evaluator(variables, functions, string): # Handle variables passed in. E.g. if we have {'R':0.5}, we make the substitution. # Special case for no variables because of how we understand PyParsing is put together - if len(variables)>0: - varnames = sreduce(lambda x,y:x|y, map(lambda x: CaselessLiteral(x), variables.keys())) - varnames.setParseAction(lambda x:map(lambda y:variables[y], x)) + if len(all_variables)>0: + varnames = sreduce(lambda x,y:x|y, map(lambda x: CaselessLiteral(x), all_variables.keys())) + varnames.setParseAction(lambda x:map(lambda y:all_variables[y], x)) else: varnames=NoMatch() # Same thing for functions. - if len(functions)>0: - funcnames = sreduce(lambda x,y:x|y, map(lambda x: CaselessLiteral(x), functions.keys())) + if len(all_functions)>0: + funcnames = sreduce(lambda x,y:x|y, map(lambda x: CaselessLiteral(x), all_functions.keys())) function = funcnames+lpar.suppress()+expr+rpar.suppress() function.setParseAction(func_parse_action) else: @@ -119,7 +143,7 @@ def evaluator(variables, functions, string): if __name__=='__main__': variables={'R1':2.0, 'R3':4.0} - functions={'sin':math.sin, 'cos':math.cos} + functions={'sin':numpy.sin, 'cos':numpy.cos} print "X",evaluator(variables, functions, "10000||sin(7+5)-6k") print "X",evaluator(variables, functions, "13") print evaluator({'R1': 2.0, 'R3':4.0}, {}, "13") diff --git a/util/views.py b/util/views.py index 5f0400d143..83d525708a 100644 --- a/util/views.py +++ b/util/views.py @@ -25,7 +25,7 @@ def calculate(request): 'equation':equation} track.views.server_track(request, 'error:calc', event, page='calc') return HttpResponse(json.dumps({'result':'Invalid syntax'})) - return HttpResponse(json.dumps({'result':result})) + return HttpResponse(json.dumps({'result':str(result)})) def send_feedback(request): ''' Feeback mechanism in footer of every page. '''