def EvaluateWithKets(variables, functions, math_expr, case_sensitive=False): ''' Evaluate expression with quantum mechanical "kets" in the expression. Each "ket" is of the form |...> and represents a unit vector. We assume the kets are either orthogonal or equal to each other: orthogonal when the labels are different, and equal when the labels are equal. ''' # avoid all XML encoding issues by not having greater-than sign or ampersand in textr gtch = chr(62) ampch = chr(38) math_expr = math_expr.replace(ampch + 'gt;', gtch) # extract list of all kets kvlist = map(KetVector, re.findall('\|[^{0}|]+{0}'.format(gtch), math_expr)) kvdict = {kv.kvstring: kv for kv in kvlist} mehex = str(map(ord, math_expr)) # for debugging if 0: raise Exception('type=%s, vcnt=%d, gcnt=%d, len=%d, math_expr=%s, kvlist=%s' % (type(math_expr), math_expr.count('|'), math_expr.count(gtch), len(kvlist), mehex, str(kvlist).replace('>','>'))) raise Exception('kvdict=%s' % str(kvdict).replace('>','>')) if not kvlist: return VectorState({None: evaluator(variables, functions, math_expr, case_sensitive)}) # get set of unique kets, after evaluating their labels try: kvlabels = set([ kv.evaluator(variables, functions, case_sensitive) for kv in kvlist ]) except Exception as err: raise Exception('Cannot evaluate ket label, err=%s' % err) # raise Exception('labels=%s' % kvlabels) # for each unique label, get coefficient # do this by setting all but that ket to zero and evaulating the whole math expression coefficient = {} for label in kvlabels: def kvsub(match): kv = kvdict[match.group(1)] if kv.has_label(label): return '1' return '0' new_expr = re.sub('(\|[^>|]+>)', kvsub, math_expr) # print " label=%s, new_expr=%s" % (label, new_expr) coefficient[label] = evaluator(variables, functions, new_expr, case_sensitive) vs = VectorState(coefficient) # print "%s -> %s" % (math_expr, vs) return vs
def compare_with_tolerance(complex1, complex2, tolerance=default_tolerance, relative_tolerance=False): """ Compare complex1 to complex2 with maximum tolerance tol. If tolerance is type string, then it is counted as relative if it ends in %; otherwise, it is absolute. - complex1 : student result (float complex number) - complex2 : instructor result (float complex number) - tolerance : string representing a number or float - relative_tolerance: bool, used when`tolerance` is float to explicitly use passed tolerance as relative. Default tolerance of 1e-3% is added to compare two floats for near-equality (to handle machine representation errors). Default tolerance is relative, as the acceptable difference between two floats depends on the magnitude of the floats. (http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) Examples: In [183]: 0.000016 - 1.6*10**-5 Out[183]: -3.3881317890172014e-21 In [212]: 1.9e24 - 1.9*10**24 Out[212]: 268435456.0 """ def myabs(elem): if isinstance(elem, numpy.matrix): return numpy.sum(abs(elem)) return abs(elem) if isinstance(tolerance, numbers.Number): tolerance = str(tolerance) if relative_tolerance: tolerance = tolerance * max(myabs(complex1), myabs(complex2)) elif tolerance.endswith('%'): tolerance = evaluator(dict(), dict(), tolerance[:-1]) * 0.01 tolerance = tolerance * max(myabs(complex1), myabs(complex2)) else: tolerance = evaluator(dict(), dict(), tolerance) try: if numpy.isinf(complex1).any() or numpy.isinf(complex2).any(): # If an input is infinite, we can end up with `abs(complex1-complex2)` and # `tolerance` both equal to infinity. Then, below we would have # `inf <= inf` which is a fail. Instead, compare directly. cmp = (complex1 == complex2) if isinstance(cmp, numpy.matrix): return cmp.all() return cmp else: # v1 and v2 are, in general, complex numbers: # there are some notes about backward compatibility issue: see responsetypes.get_staff_ans()). # return abs(complex1 - complex2) <= tolerance # # sum() used to handle matrix comparisons return numpy.sum(abs(complex1 - complex2)) <= tolerance except Exception as err: print "failure in comparison, complex1=%s, complex2=%s" % (complex1, complex2) print "err = ", err raise
def matrix_evaluator(variables, functions, math_expr, case_sensitive=False): ''' Do same as normal evaluator, but override some functions with ones which can handle matrices, like expm for matrix exponentiation. ''' #mfunctions = {'exp': mfun(scipy.linalg.expm3), mfunctions = {'exp': mfun(scipy.linalg.expm), 'cos': mfun(scipy.linalg.cosm), 'sin': mfun(scipy.linalg.sinm), 'tan': mfun(scipy.linalg.tanm), 'sqrt': mfun(scipy.linalg.sqrtm), } return evaluator(variables, mfunctions, math_expr, case_sensitive)
def matrix_evaluator(variables, functions, math_expr, case_sensitive=False): ''' Do same as normal evaluator, but override some functions with ones which can handle matrices, like expm for matrix exponentiation. ''' #mfunctions = {'exp': mfun(scipy.linalg.expm3), mfunctions = { 'exp': mfun(scipy.linalg.expm), 'cos': mfun(scipy.linalg.cosm), 'sin': mfun(scipy.linalg.sinm), 'tan': mfun(scipy.linalg.tanm), 'sqrt': mfun(scipy.linalg.sqrtm), } return evaluator(variables, mfunctions, math_expr, case_sensitive)
def evaluator(self, variables, functions, case_sensitive=False): exprs = self.kvstring[1:-1].split(',') self.label = tuple([ evaluator(variables, functions, x, case_sensitive) for x in exprs ]) return self.label