def exact_match(test_expr, target_expr): """Test if the entered expression exactly matches the known expression. This equality checking does not expand brackets or perform much simplification, so is useful for checking if the submitted answer requires simplifying. Testing is first done using '==' which checks that the order of symbols matches and will not recognise 'x + 1' as equal to '1 + x'. The 'srepr' method outputs sympy's internal representation in a canonical form and thus, while performing no simplification, it allows ordering to be ignored in exact match checking. These two forms are treated equivalently as 'exact' matching. Returns True if the sympy expressions have the same internal structure, and False if not. - 'test_expr' should be the untrusted sympy expression to check. - 'target_expr' should be the trusted sympy expression to match against. """ print "[EXACT TEST]" if test_expr == target_expr: print "Exact Match (with '==')" return True elif sympy.srepr(test_expr) == sympy.srepr(target_expr): print "Exact Match (with 'srepr')" return True else: return False
def __str__(self): asstr = "Objective: " asstr += srepr(self.objective) asstr += "\n\n" asstr += "Subject to:\n" for c in self._constraints: asstr += srepr(c) asstr += "\n" return asstr
def test_complex_space(): c1 = ComplexSpace(2) assert isinstance(c1, ComplexSpace) assert c1.dimension == 2 assert sstr(c1) == "C(2)" assert srepr(c1) == "ComplexSpace(Integer(2))" n = Symbol("n") c2 = ComplexSpace(n) assert isinstance(c2, ComplexSpace) assert c2.dimension == n assert sstr(c2) == "C(n)" assert srepr(c2) == "ComplexSpace(Symbol('n'))" assert c2.subs(n, 2) == ComplexSpace(2)
def test_create_f(): i, j, n, m = symbols('i,j,n,m') o = Fd(i) assert isinstance(o, CreateFermion) o = o.subs(i, j) assert o.atoms(Symbol) == {j} o = Fd(1) assert o.apply_operator(FKet([n])) == FKet([1, n]) assert o.apply_operator(FKet([n])) == -FKet([n, 1]) o = Fd(n) assert o.apply_operator(FKet([])) == FKet([n]) vacuum = FKet([], fermi_level=4) assert vacuum == FKet([], fermi_level=4) i, j, k, l = symbols('i,j,k,l', below_fermi=True) a, b, c, d = symbols('a,b,c,d', above_fermi=True) p, q, r, s = symbols('p,q,r,s') assert Fd(i).apply_operator(FKet([i, j, k], 4)) == FKet([j, k], 4) assert Fd(a).apply_operator(FKet([i, b, k], 4)) == FKet([a, i, b, k], 4) assert Dagger(B(p)).apply_operator(q) == q*CreateBoson(p) assert repr(Fd(p)) == 'CreateFermion(p)' assert srepr(Fd(p)) == "CreateFermion(Symbol('p'))" assert latex(Fd(p)) == r'a^\dagger_{p}'
def save_sympy_matrix(matrix, filename): """Writes a matrix to file in the SymPy representation (srepr).""" num_rows, num_cols = matrix.shape with open(filename, 'w') as f: f.write(str(num_rows) + "\n") f.write(str(num_cols) + "\n") for expr in matrix: f.write(srepr(expr) + "\n")
def test_fixed_bosonic_basis(): b = FixedBosonicBasis(2, 2) # assert b == [FockState((2, 0)), FockState((1, 1)), FockState((0, 2))] state = b.state(1) assert state == FockStateBosonKet((1, 1)) assert b.index(state) == 1 assert b.state(1) == b[1] assert len(b) == 3 assert str(b) == '[FockState((2, 0)), FockState((1, 1)), FockState((0, 2))]' assert repr(b) == '[FockState((2, 0)), FockState((1, 1)), FockState((0, 2))]' assert srepr(b) == '[FockState((2, 0)), FockState((1, 1)), FockState((0, 2))]'
def test_annihilate_f(): i, j, n, m = symbols('i,j,n,m') o = F(i) assert isinstance(o, AnnihilateFermion) o = o.subs(i, j) assert o.atoms(Symbol) == {j} o = F(1) assert o.apply_operator(FKet([1, n])) == FKet([n]) assert o.apply_operator(FKet([n, 1])) == -FKet([n]) o = F(n) assert o.apply_operator(FKet([n])) == FKet([]) i, j, k, l = symbols('i,j,k,l', below_fermi=True) a, b, c, d = symbols('a,b,c,d', above_fermi=True) p, q, r, s = symbols('p,q,r,s') assert F(i).apply_operator(FKet([i, j, k], 4)) == 0 assert F(a).apply_operator(FKet([i, b, k], 4)) == 0 assert F(l).apply_operator(FKet([i, j, k], 3)) == 0 assert F(l).apply_operator(FKet([i, j, k], 4)) == FKet([l, i, j, k], 4) assert str(F(p)) == 'f(p)' assert repr(F(p)) == 'AnnihilateFermion(p)' assert srepr(F(p)) == "AnnihilateFermion(Symbol('p'))" assert latex(F(p)) == 'a_{p}'
def findForwarBackward(formula): fragments = findBrackets(formula) for i in range(len(fragments)): #formula = re.sub("(\w)"+re.escape(fragments[0]),r"\1_fragmentNb%d_"%i,formula) formula = formula.replace(fragments[i],"_fragmentNb%d_"%i) variables = unique(re.findall("_?[A-Za-z]\w*",formula)) command = ",".join(variables)+" = sympy.symbols(\"" + ",".join(variables) + "\")" exec(command) formula = sympy.expand(formula) backward = [] forward = [] additiveBlocks = sympy.srepr(formula) if additiveBlocks[0:3] == "Add": additiveBlocks = sympy.sympify(additiveBlocks[4:len(additiveBlocks)-1]) else: additiveBlocks = [sympy.sympify(additiveBlocks)] for ee in additiveBlocks: ee = str(sympy.simplify(ee)) ee = str(ee) if ee[0] == "-": ee = re.sub("^-","",ee) backward.append(ee) else: forward.append(ee) backward = "+".join(backward) forward = "+".join(forward) for i in range(len(fragments)): backward = backward.replace("_fragmentNb%d_"%i,fragments[i]) forward = forward.replace("_fragmentNb%d_"%i,fragments[i]) # backward = re.sub("(?<!\w)fragmentNb%d(?!\w)"%i,fragments[i],backward) # forward = re.sub("(?<!\w)fragmentNb%d(?!\w)"%i,fragments[i],forward) return(forward,backward)
def __eq__(self, other): from sympy import srepr return (isinstance(other, ParameterExpression) and self.parameters == other.parameters and srepr(self._symbol_expr) == srepr(other._symbol_expr))
g = self.g g_inv = self.g.inv() scalar_curv = sympy.simplify(g_inv*Ricci) scalar_curv = sympy.trace(scalar_curv) self.scalar_curv = scalar_curv return self.scalar_curv if __name__ == "__main__": import find_metric k = -1 g,diff_form = find_metric.analytical(k) # k=0 gives flat space R = Riemann(g,dim=3,sys_title="analytical") print R.metric from sympy import srepr print srepr(R.system) RC = R.find_Christoffel_tensor() RR = R.find_Ricci_tensor() scalarRR = R.find_scalar_curvature() print "\nThe analytical curve element has the following metric for k=%.1f"%k print g print "\nThe Ricci tensor is given as" print RR print "\nand the scalar curvature is" print scalarRR """ from sympy.abc import r,theta, phi, u,v g,diff_form = find_metric.flat_sphere() diff = [['dv*dv','dv*dw'],['dw*dv','dw*dw']]
def test_issue1689(): assert srepr(S(1.0 + 0J)) == srepr(S(1.0)) == srepr(Float(1.0)) assert srepr(Float(1)) != srepr(Float(1.0))
def test_hilbert_space(): hs = HilbertSpace() assert isinstance(hs, HilbertSpace) assert sstr(hs) == "H" assert srepr(hs) == "HilbertSpace()"
def d(obj): print("WTF", type(obj), srepr(obj))
def print_conversions(*modules): for mod in modules: for x in module_conversions(mod): print '("' + mod.NAME + '", "%s", "%s"): "%s"' % (x[0], x[1], srepr(x[2]))
def print_conversions(*modules): for mod in modules: for x in module_conversions(mod): print '("' + mod.NAME + '", "%s", "%s"): "%s"' % ( x[0], x[1], srepr(x[2]))
def _get_srepr(expr): s = srepr(expr) s = re.sub(r"WildDot\('(\w+)'\)", r"\1", s) s = re.sub(r"WildPlus\('(\w+)'\)", r"*\1", s) s = re.sub(r"WildStar\('(\w+)'\)", r"*\1", s) return s
def exprstrout(key, expr): return srepr(expr) + " " + PolynomialOutput.messages.get(key, None)
def octoutput(x, et): OCTCODE_INT = 1001 OCTCODE_DOUBLE = 1002 OCTCODE_STR = 1003 OCTCODE_BOOL = 1005 OCTCODE_COMPLEX = 1006 OCTCODE_DICT = 1010 OCTCODE_SYM = 1020 x = objectfilter(x) if isinstance(x, bool): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_BOOL) f = ET.SubElement(a, "f") f.text = str(x) elif x is None or isinstance(x, (sp.Basic, sp.MatrixBase)): # FIXME: is it weird to pretend None is a SymPy object? if isinstance(x, (sp.Matrix, sp.ImmutableMatrix)): _d = x.shape elif isinstance(x, sp.MatrixExpr): # nan for symbolic size _d = [float(r) if (isinstance(r, sp.Basic) and r.is_Integer) else float('nan') if isinstance(r, sp.Basic) else r for r in x.shape] elif x is None: _d = (1,1) else: _d = (1, 1) try: pretty_ascii = sp.pretty(x, use_unicode=False) except: # e.g., SymPy issue #10414 pretty_ascii = str(x) pretty_unicode = sp.pretty(x, use_unicode=True) a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_SYM) f = ET.SubElement(a, "f") f.text = sympy.srepr(x) f = ET.SubElement(a, "f") f.text = str(_d[0]) f = ET.SubElement(a, "f") f.text = str(_d[1]) f = ET.SubElement(a, "f") f.text = str(x) # esc? f = ET.SubElement(a, "f") f.text = myesc(pretty_ascii) f = ET.SubElement(a, "f") f.text = myesc(pretty_unicode) elif isinstance(x, (list, tuple)): c = ET.SubElement(et, "list") for y in x: octoutput(y, c) elif isinstance(x, sp.compatibility.integer_types): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_INT) f = ET.SubElement(a, "f") f.text = str(x) elif isinstance(x, float): # We pass IEEE doubles using the exact hex representation a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_DOUBLE) f = ET.SubElement(a, "f") f.text = d2hex(x) elif isinstance(x, complex): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_COMPLEX) f = ET.SubElement(a, "f") f.text = d2hex(x.real) f = ET.SubElement(a, "f") f.text = d2hex(x.imag) elif isinstance(x, sp.compatibility.string_types): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_STR) f = ET.SubElement(a, "f") f.text = myesc(x) elif isinstance(x, dict): # Note: the dict cannot be too complex, keys must convert to # strings for example. Values can be dicts, lists. a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_DICT) # Convert keys to strings keystr = [str(y) for y in x.keys()] c = ET.SubElement(a, "list") octoutput(keystr, c) c = ET.SubElement(a, "list") # FIXME: bit of a kludge, use iterable instead of list, tuple above? if sys.version_info >= (3, 0): octoutput(list(x.values()), c) else: octoutput(x.values(), c) else: raise ValueError("octoutput does not know how to export type " + str(type(x)))
def latexParsing(token, tokenPos): ''' LateX parsing function for DIM files ''' lastPos = 0 # Each line gets stored as a separate list item in the list l_depTokens = [] l_posTokens = [] l_morTokens = [] stringRep = '' # Try parsing the latex code as is if len(token) == 14: try: expr = parse_latex(LATEXMAP[token]) stringRep = srepr(expr) except (LaTeXParsingError, SympifyError, TypeError): # Good chance the problem is the leading and trailing parens - # remove them and try again try: expr = parse_latex(LATEXMAP[token].lstrip('(').rstrip(')')) stringRep = srepr(expr) except (LaTeXParsingError, ValueError, TypeError): pass # If we have a sympy string representation... if stringRep != '': # Problematic artefacts from sympy parsing stringRep = stringRep.replace(", precision=53", "") stringRep = stringRep.replace("oo", "Symbol(inf)") # Call gov_dep function to get list of dependencies, objects l_dependencies = (gov_dep(stringRep)) # This will store each dependency item dictAll = {} # If we actually have a list of items rather than a single # symbol/integer if len(l_dependencies) > 0: # Do the D in DIM for li in l_dependencies: head = li[0] tail = li[1] dictAll[head[1]] = head[0] dictAll[tail[1]] = tail[0] l_depTokens.append( (get_rel(head[0]), (head[0], head[1] + tokenPos - 1), (tail[0], tail[1] + tokenPos - 1))) if head[1] > lastPos: lastPos = head[1] if tail[1] > lastPos: lastPos = tail[1] # We're dealing with just a symbol or integer else: # Keep track of govs for debugging dictAll[1] = stringRep lastPos = 1 # Do the I and M in DIM for key, val in dictAll.items(): # IF it's a symbol/integer if '(' in val: symbol, symbolType = get_symbol_and_type(val) l_posTokens.append('{}_{}'.format(val, symbolType.upper())) l_morTokens.append(val) else: thisPos = get_rel(val) thisPos = thisPos.upper() l_posTokens.append('{}_{}'.format(val, thisPos)) l_morTokens.append(val) return l_depTokens, l_posTokens, l_morTokens
def octoutput(x, et): OCTCODE_INT = 1001 OCTCODE_DOUBLE = 1002 OCTCODE_STR = 1003 OCTCODE_BOOL = 1005 OCTCODE_COMPLEX = 1006 OCTCODE_DICT = 1010 OCTCODE_SYM = 1020 x = objectfilter(x) if isinstance(x, bool): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_BOOL) f = ET.SubElement(a, "f") f.text = str(x) elif x is None or isinstance(x, (sp.Basic, sp.MatrixBase)): # FIXME: is it weird to pretend None is a SymPy object? if isinstance(x, (sp.Matrix, sp.ImmutableMatrix)): _d = x.shape elif isinstance(x, sp.MatrixExpr): # nan for symbolic size _d = [float(r) if (isinstance(r, sp.Basic) and r.is_Integer) else float('nan') if isinstance(r, sp.Basic) else r for r in x.shape] elif x is None: _d = (1,1) else: _d = (1, 1) try: pretty_ascii = sp.pretty(x, use_unicode=False) except: # e.g., SymPy issue #10414 pretty_ascii = str(x) pretty_unicode = sp.pretty(x, use_unicode=True) a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_SYM) f = ET.SubElement(a, "f") f.text = sympy.srepr(x) f = ET.SubElement(a, "f") f.text = str(_d[0]) f = ET.SubElement(a, "f") f.text = str(_d[1]) f = ET.SubElement(a, "f") f.text = str(x) # esc? f = ET.SubElement(a, "f") f.text = myesc(pretty_ascii) f = ET.SubElement(a, "f") f.text = myesc(pretty_unicode) elif isinstance(x, (list, tuple)): c = ET.SubElement(et, "list") for y in x: octoutput(y, c) elif isinstance(x, int): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_INT) f = ET.SubElement(a, "f") f.text = str(x) elif isinstance(x, float): # We pass IEEE doubles using the exact hex representation a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_DOUBLE) f = ET.SubElement(a, "f") f.text = d2hex(x) elif isinstance(x, complex): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_COMPLEX) f = ET.SubElement(a, "f") f.text = d2hex(x.real) f = ET.SubElement(a, "f") f.text = d2hex(x.imag) elif isinstance(x, str) or (sys.version_info < (3, 0) and isinstance(x, unicode)): a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_STR) f = ET.SubElement(a, "f") f.text = myesc(x) elif isinstance(x, dict): # Note: the dict cannot be too complex, keys must convert to # strings for example. Values can be dicts, lists. a = ET.SubElement(et, "item") f = ET.SubElement(a, "f") f.text = str(OCTCODE_DICT) # Convert keys to strings keystr = [str(y) for y in x.keys()] c = ET.SubElement(a, "list") octoutput(keystr, c) c = ET.SubElement(a, "list") # FIXME: bit of a kludge, use iterable instead of list, tuple above? if sys.version_info >= (3, 0): octoutput(list(x.values()), c) else: octoutput(x.values(), c) else: raise ValueError("octoutput does not know how to export type " + str(type(x)))
def __str__(self): return "%s.%s %s %s" % (self.ibits, self.q, self.scale, srepr( self.expr))
def varExprNames(expression): variables_Str = list(set(findall(r"Symbol\(\'(\w+)\'\)", srepr(expression)))) return [var_name for var_name in variables_Str]
def __init__(self, file, name, noise=None, seed=0, preprocess=None, function_lib=None, extra_data_dir=None, dataset_size_multiplier=None, **kwargs): # Read in benchmark dataset information root = resource_filename("dsr", "data/") benchmark_path = os.path.join(root, file) df = pd.read_csv(benchmark_path, index_col=0, encoding="ISO-8859-1") # Random number generator used for sampling X values seed += zlib.adler32(name.encode("utf-8")) # Different seed for each name, otherwise two benchmarks with the same domain will always have the same X values self.rng = np.random.RandomState(seed) self.dataset_size_multiplier = dataset_size_multiplier if dataset_size_multiplier is not None else 1.0 # Load raw dataset from external directory root_changed = False if extra_data_dir is not None: root = os.path.join(extra_data_dir,"") root_changed = True # Raw dataset if name not in df.index: if noise is not None and noise != 0: print("Warning: Noise will not be applied to real-world dataset.") dataset_path = os.path.join(root, name + ".csv") data = pd.read_csv(dataset_path) data = data.sample(frac=1, random_state=self.rng).reset_index(drop=True) # Shuffle rows data = data.values if preprocess == "standardize_y": mean = np.mean(data[:, -1]) std = np.std(data[:, -1]) data[:, -1] = (data[:, -1] - mean) / std elif preprocess == "standardize_all": data = (data - np.mean(data, axis=0)) / np.std(data, axis=0) elif preprocess == "shift_y": mean = np.mean(data[:, -1]) data[:, -1] = data[:, -1] - mean elif preprocess == "shift_all": data = data - np.mean(data, axis=0) elif preprocess == "rescale_y": min_ = min(data[:, -1]) max_ = max(data[:, -1]) data[:, -1] = (data[:, -1] - min_) / (max_ - min_) elif preprocess == "rescale_all": min_ = np.min(data, axis=0) max_ = np.max(data, axis=0) data = (data - min_) / (max_ - min_) self.n_input_var = data.shape[1] - 1 n_train = int(0.8 * data.shape[0]) # 80-20 train-test split self.X_train = data[:n_train, :-1] self.y_train = data[:n_train, -1] self.X_test = data[n_train:, :-1] self.y_test = data[n_train:, -1] self.y_train_noiseless = self.y_train.copy() self.y_test_noiseless = self.y_test.copy() self.sympy_expr = None self.numpy_expr = None self.fp_constant = None self.int_constant = None self.train_spec = None self.test_spec = None function_set = "Real" # Default value of library # Expression dataset else: row = df.loc[name] function_set = row["function_set"] self.n_input_var = row["variables"] # Create symbolic expression self.sympy_expr = parse_expr(row["sympy"]) self.numpy_expr = self.make_numpy_expr(row["numpy"]) self.fp_constant = "Float" in srepr(self.sympy_expr) self.int_constant = "Integer" in srepr(self.sympy_expr) # Create X values train_spec = ast.literal_eval(row["train_spec"]) test_spec = ast.literal_eval(row["test_spec"]) if test_spec is None: test_spec = train_spec self.X_train = self.make_X(train_spec) self.X_test = self.make_X(test_spec) self.train_spec = train_spec self.test_spec = test_spec # Compute y values self.y_train = self.numpy_expr(self.X_train) self.y_test = self.numpy_expr(self.X_test) self.y_train_noiseless = self.y_train.copy() self.y_test_noiseless = self.y_test.copy() # Add Gaussian noise if noise is not None: assert noise >= 0, "Noise must be non-negative." y_rms = np.sqrt(np.mean(self.y_train**2)) scale = noise * y_rms self.y_train += self.rng.normal(loc=0, scale=scale, size=self.y_train.shape) self.y_test += self.rng.normal(loc=0, scale=scale, size=self.y_test.shape) # If root has changed if root_changed: root = resource_filename("dsr", "data/") # Create the function set (list of str) function_set_path = os.path.join(root, "function_sets.csv") df = pd.read_csv(function_set_path, index_col=0) self.function_set = df.loc[function_set].tolist()[0].strip().split(',') # Overwrite the function set if function_lib is not None: self.function_set = function_lib.strip().split(',')
def exprstrout(key, expr): return srepr(expr) + " " + SingletonOutput.messages.get(key, None)
def expr_convert_to_SIMD_intrins(expr, SIMD_const_varnms,SIMD_const_values,SIMD_const_suffix="",debug="False"): # Declare all variables, so we can eval them in the next (AddSIMD & MulSIMD) step for item in preorder_traversal(expr): for i in range(len(item.args)): if isinstance(item.args[i],Symbol): var(str(item.args[i])) expr_orig = expr AbsSIMD = Function("AbsSIMD") AddSIMD = Function("AddSIMD") SubSIMD = Function("SubSIMD") MulSIMD = Function("MulSIMD") FusedMulAddSIMD = Function("FusedMulAddSIMD") FusedMulSubSIMD = Function("FusedMulSubSIMD") DivSIMD = Function("DivSIMD") SignSIMD = Function("SignSIMD") PowSIMD = Function("PowSIMD") SqrtSIMD = Function("SqrtSIMD") CbrtSIMD = Function("CbrtSIMD") ExpSIMD = Function("ExpSIMD") LogSIMD = Function("LogSIMD") SinSIMD = Function("SinSIMD") CosSIMD = Function("CosSIMD") # Step 1: Replace transcendental, power, and division functions with SIMD equivalents # Note that due to how SymPy expresses rational numbers, the following does not # affect fractional expressions of integers for item in preorder_traversal(expr): if item.func == Abs: expr = expr.xreplace({item: AbsSIMD(item.args[0])}) elif item.func == exp: expr = expr.xreplace({item: ExpSIMD(item.args[0])}) elif item.func == log: expr = expr.xreplace({item: LogSIMD(item.args[0])}) elif item.func == sin: expr = expr.xreplace({item: SinSIMD(item.args[0])}) elif item.func == cos: expr = expr.xreplace({item: CosSIMD(item.args[0])}) elif item.func == sign: expr = expr.xreplace({item: SignSIMD(item.args[0])}) # Fun little recursive function for constructing integer powers: def IntegerPowSIMD(a, n): if n == 2: return MulSIMD(a, a) elif n > 2: return MulSIMD(IntegerPowSIMD(a, n - 1), a) elif n <= -2: return DivSIMD(1, IntegerPowSIMD(a, -n)) elif n == -1: return DivSIMD(1, a) for item in preorder_traversal(expr): if item.func == Pow: if item.args[1] == 0.5: expr = expr.xreplace({item: SqrtSIMD(item.args[0])}) elif item.args[1] == -0.5: expr = expr.xreplace({item: DivSIMD(1,SqrtSIMD(item.args[0]))}) elif item.args[1] == Rational(1,3): expr = expr.xreplace({item: CbrtSIMD(item.args[0])}) elif item.args[1] == int(item.args[1]): expr = expr.xreplace({item: IntegerPowSIMD(item.args[0], item.args[1])}) else: expr = expr.xreplace({item: PowSIMD(item.args[0], item.args[1])}) # Step 2: Replace all rational numbers (expressed as Rational(a,b)) # and integers with the new functions RationalTMP and # IntegerTMP, where Rational(a,b) -> RationalTMP(a,b) # and Integer(a) -> IntegerTMP(a) RationalTMP = Function("RationalTMP") IntegerTMP = Function("IntegerTMP") string = str(srepr(expr)) string2 = re.sub(r'Integer\(([+\-0-9]+)\)', "(Function('IntegerTMP')('\\1'))", string) expr = eval(string2) string = str(srepr(expr)) string2 = re.sub(r'Rational\(([-0-9]+), ([0-9]+)\)', "(Function('RationalTMP')(('\\1'),('\\2')))", string) expr = eval(string2) # Step 3: The pattern Mul(-1,Rational(a,b)) is often seen. Replace with Rational(-a,b). for item in preorder_traversal(expr): if item.func == Mul: idx_has_Negative1 = -10 for i in range(len(item.args)): if item.args[i].func == IntegerTMP and item.args[i].args[0] == -1: idx_has_Negative1 = i if idx_has_Negative1 >= 0: for i in range(len(item.args)): if item.args[i].func==RationalTMP: tempitem_orig = Mul(IntegerTMP(-1),RationalTMP(item.args[i].args[0], item.args[i].args[1])) tempitem_new = RationalTMP(-item.args[i].args[0], item.args[i].args[1]) expr = expr.subs(tempitem_orig, tempitem_new ) break # Step 4: SIMD multiplication and addition compiler intrinsics read in # only two arguments at once, where SymPy's Mul() and Add() # operators can read an arbitrary number of arguments. # Here, we split e.g., Mul(a,b,c,d) into # MulSIMD(a,MulSIMD(b,MulSIMD(c,d))), # To accomplish this easily, we construct a string # 'MulSIMD(A,MulSIMD(B,...', where MulSIMD(a,b) is some user- # defined function that takes in only two arguments, and then # evaluate the string using the eval() function. # Implementation detail: If we did not perform Step 2 above, the eval # function would automatically evaluate all Rational expressions # as though they were input as integers: e.g., 1/2 evaluates to 0. # This is undesirable, so we instead define new, temporary # functions IntegerTMP and RationalTMP that are undisturbed by # the eval() for item in preorder_traversal(expr): if item.func == Mul: blahtmp = symbols('blahtmp') tempitem = blahtmp for i in range(len(item.args)-1): tempitem = MulSIMD(item.args[i],tempitem) tempitem = tempitem.xreplace({blahtmp: item.args[len(item.args)-1]}) expr = expr.xreplace({item: tempitem}) for item in preorder_traversal(expr): if item.func == Add: blahtmp = symbols('blahtmp') tempitem = blahtmp for i in range(len(item.args)-1): tempitem = AddSIMD(item.args[i],tempitem) tempitem = tempitem.xreplace({blahtmp: item.args[len(item.args)-1]}) expr = expr.xreplace({item: tempitem}) # Step 5: Simplification patterns: # Step 5a: Replace the pattern Mul(Div(1,b),a) or Mul(a,Div(1,b)) with Div(a,b): for item in preorder_traversal(expr): if item.func == MulSIMD: if item.func == MulSIMD and (item.args[0].func == DivSIMD and item.args[0].args[0].func == IntegerTMP and item.args[0].args[0].args[0] == 1): expr = expr.xreplace({item: DivSIMD(item.args[1],item.args[0].args[1])}) if item.func == MulSIMD and (item.args[1].func == DivSIMD and item.args[1].args[0].func == IntegerTMP and item.args[1].args[0].args[0] == 1): expr = expr.xreplace({item: DivSIMD(item.args[0],item.args[1].args[1])}) # Step 5: Subtraction intrinsics. SymPy replaces all a-b with a + (-b) = Add(a,Mul(-1,b)) # Here, we replace # a) AddSIMD(a,MulSIMD(-1,b)), # b) AddSIMD(a,MulSIMD(b,-1)), # c) AddSIMD(MulSIMD(-1,b),a), and # d) AddSIMD(MulSIMD(b,-1),a) # with SubSIMD(a,b) for item in preorder_traversal(expr): tempitem = item # First match patterns a) and b): AddSIMD(a,MULSIMD(.,.)): if item.func == AddSIMD and item.args[1].func == MulSIMD: # Pattern a) AddSIMD(a,MulSIMD(-1,b)) --> SubSIMD(a,b): if item.args[1].args[0].func == IntegerTMP and item.args[1].args[0].args[0] == -1: tempitem = SubSIMD(item.args[0],item.args[1].args[1]) # Pattern b) AddSIMD(a,MulSIMD(b,-1)) --> SubSIMD(a,b): elif item.args[1].args[1].func == IntegerTMP and item.args[1].args[1].args[0] == -1: tempitem = SubSIMD(item.args[0],item.args[1].args[0]) # Next match patterns c) and d): AddSIMD(MulSIMD(.,.),a): elif item.func == AddSIMD and item.args[0].func == MulSIMD: # Pattern c) AddSIMD(MulSIMD(-1,b),a) --> SubSIMD(a,b): if item.args[0].args[0].func == IntegerTMP and item.args[0].args[0].args[0] == -1: tempitem = SubSIMD(item.args[1],item.args[0].args[1]) # Pattern d) AddSIMD(MulSIMD(b,-1,a)) --> SubSIMD(a,b): elif item.args[0].args[1].func == IntegerTMP and item.args[0].args[1].args[0] == -1: tempitem = SubSIMD(item.args[1],item.args[0].args[0]) expr = expr.subs(item,tempitem) # Step 6: Now that all multiplication and addition functions only take two # arguments, we can now easily define fused-multiply-add functions, # where AddSIMD(a,MulSIMD(b,c)) = b*c + a = FusedMulAddSIMD(b,c,a), # or AddSIMD(MulSIMD(b,c),a) = b*c + a = FusedMulAddSIMD(b,c,a). # Fused multiply add (FMA3) is standard on Intel CPUs with the AVX2 # instruction set, starting with Haswell processors in 2013: # https://en.wikipedia.org/wiki/Haswell_(microarchitecture) for item in preorder_traversal(expr): tempitem = item # If the pattern is a*b + c, replace with FMA(a,b,c) if item.func == AddSIMD and item.args[0].func == MulSIMD: tempitem = FusedMulAddSIMD(item.args[0].args[0],item.args[0].args[1],item.args[1]) # If the pattern is c + a*b, replace with FMA(a,b,c) if item.func == AddSIMD and item.args[1].func == MulSIMD: tempitem = FusedMulAddSIMD(item.args[1].args[0],item.args[1].args[1],item.args[0]) # If the pattern is a*b - c, replace with FMS(a,b,c) if item.func == SubSIMD and item.args[0].func == MulSIMD: tempitem = FusedMulSubSIMD(item.args[0].args[0],item.args[0].args[1],item.args[1]) if item != tempitem: expr = expr.subs(item, tempitem) # Step 7: SIMD intrinsics cannot take integers or rational numbers as arguments. # Therefore we must declare all integers & rational numbers as # const vector doubles (e.g., const _m256d ...). To make the code # more human readable, we adopt the convention # RationalTMP(1,3) = 1/3 = "Rational_1_3 # RationalTMP(-1,3) = -1/3 = "Rational_m1_3 # TODO: Keep track of all integers and rationals, so repeats are not computed? # Step 7a: Set all variable names and corresponding values. for item in preorder_traversal(expr): if item.func == RationalTMP: # Set variable name if item.args[0]*item.args[1] < 0: SIMD_const_varnms.extend(["_Rational_m"+str(abs(item.args[0]))+"_"+str(abs(item.args[1]))+SIMD_const_suffix]) elif item.args[0] > 0 and item.args[1] > 0: SIMD_const_varnms.extend(["_Rational_"+str(item.args[0])+"_"+str(item.args[1])+SIMD_const_suffix]) else: # E.g., doesn't make sense to have -1/-3. SymPy should have simplified this. print("Found a weird Rational(a,b) expression, where a<0 and b<0. Report to SymPy devels") print("Specifically, found that a="+str(item.args[0])+" and b="+str(item.args[1])) sys.exit(1) # Set variable value, to 34 digits of precision SIMD_const_values.extend([str(N(Float(item.args[0],34)/Float(item.args[1],34),34))]) elif item.func == IntegerTMP: # Set variable name if item.args[0] < 0: SIMD_const_varnms.extend(["_Integer_m"+str(-item.args[0])+SIMD_const_suffix]) else: SIMD_const_varnms.extend(["_Integer_" + str(item.args[0])+SIMD_const_suffix]) # Set variable value, to 34 digits of precision SIMD_const_values.extend([str((Float(item.args[0],34)))]) # Step 7b: Replace all integers and rationals with the appropriate variable names: for item in preorder_traversal(expr): tempitem = item if item.func == RationalTMP: if item.args[0]*item.args[1] < 0: tempitem = var("_Rational_m" + str(abs(item.args[0])) + "_" + str(abs(item.args[1]))+SIMD_const_suffix) elif item.args[0] > 0 and item.args[1] > 0: tempitem = var("_Rational_" + str(item.args[0]) + "_" + str(item.args[1])+SIMD_const_suffix) else: # E.g., doesn't make sense to have -1/-3. SymPy should have simplified this. print("Found a weird Rational(a,b) expression, where a<0 and b<0. Report to SymPy devels") print("Specifically, found that a=" + str(item.args[0]) + " and b=" + str(item.args[1])) sys.exit(1) elif item.func == IntegerTMP: if item.args[0] < 0: tempitem = var("_Integer_m" + str(-item.args[0])+SIMD_const_suffix) else: tempitem = var("_Integer_" + str(item.args[0])+SIMD_const_suffix) if item != tempitem: expr = expr.subs(item, tempitem) def lookup_name_output_idx(name, list_of_names): for i in range(len(list_of_names)): if list_of_names[i] == name: return i print("I SHOULDN'T BE HERE!",name,list_of_names) sys.exit(1) if debug=="True": expr_check = expr if "SIMD" in str(expr): expr_check = eval(str(expr).replace("SIMD","SIMD_check")) for item in preorder_traversal(expr_check): tempitem = item if item.is_Symbol and str(item)[0]=="_": if str(item)[:9]=="_Integer_" or str(item)[:10]=="_Rational_": tempitem = SIMD_const_values[lookup_name_output_idx(str(item), SIMD_const_varnms)] if item != tempitem: expr_check = expr_check.subs(item, tempitem) expr_diff = expr_check - expr_orig # Some variables do not want to cancel in SymPy ~0.7.4. The eval(str(srepr())) below normalizes the expression. expr_diff = eval(str(srepr(expr_diff))) for item in preorder_traversal(expr_diff): if item.func == Float: if abs(item - Integer(item)) < 1.0e-14: expr_diff = expr_diff.xreplace({item:Integer(item)}) # Only simplify if expr_diff != 0: if expr_diff != 0: simp_expr_diff = simplify(expr_diff) if simp_expr_diff != 0: print("Warning: found possible diff",(simp_expr_diff)) return(expr)
def custom_serializer(obj: Any) -> JSON: """ Serializes objects that are not JSON-serializable by default. Fallback is ``obj.__dict__``, if this does not exist the input object is returned unchanged. Numpy arrays and pandas DataFrames are handled separately. Custom classes can use a method named ``to_json()`` or a property named ``json`` that returns a ``dict`` or a string representation of a ``dict``. The class name is stored in the ``type`` attribute in the output JSON. If these attributes are not found, ``__dict__`` or ``str()`` is used. Parameters ---------- obj : Any Input object Returns ------- JSON Serializable representation of the input that preserves the nested structure. """ if isinstance(obj, Path): return {'type': 'Path', 'data': str(obj.absolute())} if isinstance(obj, Quantity): return { 'type': 'Quantity', 'data': [serialize(obj.m), str(obj.u._units)] } if isinstance(obj, pd.Series): return { 'type': 'Series', 'data': obj.to_json(orient='split', default_handler=custom_serializer) } if isinstance(obj, pd.DataFrame): return { 'type': 'DataFrame', 'data': obj.to_json(orient='split', default_handler=custom_serializer) } if isinstance(obj, np.ndarray): return { 'type': 'ndarray', 'data': [serialize(x) for x in obj.tolist()] } if isinstance(obj, Decimal): return {'type': 'Decimal', 'data': str(obj)} if isinstance(obj, AffineScalarFunc): return { 'type': 'AffineScalarFunc', 'data': [obj.nominal_value, obj.std_dev] } if isinstance(obj, sp.Basic): return {'type': 'Sympy', 'data': sp.srepr(obj)} # method named "to_json" or @property named "json" if hasattr(obj, 'to_json') or hasattr(obj, 'json'): # this method can return a dict or a string # JSON representation if hasattr(obj, 'json'): json_repr = getattr(obj, 'json') else: json_repr = obj.to_json() # make sure the object's dict representation is serializable # if this is a string this is not necessary if not isinstance(json_repr, str): json_repr = serialize(json_repr) obj_type = obj.__class__.__name__ # the custom class must implement classmethod # from_dict, which takes json_repr as input and # returns a class instance return {'type': obj_type, 'data': json_repr} if hasattr(obj, '__dict__'): return obj.__dict__ if is_serializable(obj): return obj # fallback, this cannot be deserialized return str(obj)
def __str__(self): return "FORALL " + str(self.query_symbols) + " in " + str(self.query) + ": " + srepr(self.relation)
def general_symbolic(target, eqn=None, arg_map=None): r''' A general function to interpret a sympy equation and evaluate the linear components of the source term. Parameters ---------- target : OpenPNM object The OpenPNM object where the result will be applied. eqn : sympy symbolic expression for the source terms e.g. y = a*x**b + c arg_map : Dict mapping the symbols in the expression to OpenPNM data on the target. Must contain 'x' which is the independent variable. e.g. arg_map={'a':'pore.a', 'b':'pore.b', 'c':'pore.c', 'x':'pore.x'} Example ---------- >>> import openpnm as op >>> from openpnm.models.physics import generic_source_term as gst >>> import scipy as sp >>> import sympy as _syp >>> pn = op.network.Cubic(shape=[5, 5, 5], spacing=0.0001) >>> water = op.phases.Water(network=pn) >>> water['pore.a'] = 1 >>> water['pore.b'] = 2 >>> water['pore.c'] = 3 >>> water['pore.x'] = sp.random.random(water.Np) >>> a, b, c, x = _syp.symbols('a,b,c,x') >>> y = a*x**b + c >>> arg_map = {'a':'pore.a', 'b':'pore.b', 'c':'pore.c', 'x':'pore.x'} >>> water.add_model(propname='pore.general', ... model=gst.general_symbolic, ... eqn=y, arg_map=arg_map, ... regen_mode='normal') >>> assert 'pore.general.rate' in water.props() >>> assert 'pore.general.S1' in water.props() >>> assert 'pore.general.S1' in water.props() ''' # First make sure all the symbols have been allocated dict items for arg in _syp.postorder_traversal(eqn): if _syp.srepr(arg)[:6] == 'Symbol': key = _syp.srepr(arg)[7:].strip('(').strip(')').strip("'") if key not in arg_map.keys(): raise Exception('argument mapping incomplete, missing '+key) if 'x' not in arg_map.keys(): raise Exception('argument mapping must contain "x" for the ' + 'independent variable') # Get the data data = {} args = {} for key in arg_map.keys(): data[key] = target[arg_map[key]] # Callable functions args[key] = _syp.symbols(key) r, s1, s2 = _build_func(eqn, **args) r_val = r(*data.values()) s1_val = s1(*data.values()) s2_val = s2(*data.values()) values = {'S1': s1_val, 'S2': s2_val, 'rate': r_val} return values
def general_symbolic(target, eqn=None, arg_map=None): r''' A general function to interpret a sympy equation and evaluate the linear components of the source term. Parameters ---------- target : OpenPNM object The OpenPNM object where the result will be applied. eqn : sympy symbolic expression for the source terms e.g. y = a*x**b + c arg_map : Dict mapping the symbols in the expression to OpenPNM data on the target. Must contain 'x' which is the independent variable. e.g. arg_map={'a':'pore.a', 'b':'pore.b', 'c':'pore.c', 'x':'pore.x'} Example ---------- >>> import openpnm as op >>> from openpnm.models.physics import generic_source_term as gst >>> import scipy as sp >>> import sympy >>> pn = op.network.Cubic(shape=[5, 5, 5], spacing=0.0001) >>> water = op.phases.Water(network=pn) >>> water['pore.a'] = 1 >>> water['pore.b'] = 2 >>> water['pore.c'] = 3 >>> water['pore.x'] = sp.random.random(water.Np) >>> a, b, c, x = sympy.symbols('a,b,c,x') >>> y = a*x**b + c >>> arg_map = {'a':'pore.a', 'b':'pore.b', 'c':'pore.c', 'x':'pore.x'} >>> water.add_model(propname='pore.general', ... model=gst.general_symbolic, ... eqn=y, arg_map=arg_map, ... regen_mode='normal') >>> assert 'pore.general.rate' in water.props() >>> assert 'pore.general.S1' in water.props() >>> assert 'pore.general.S1' in water.props() ''' from sympy import postorder_traversal, srepr, symbols # First make sure all the symbols have been allocated dict items for arg in postorder_traversal(eqn): if srepr(arg)[:6] == 'Symbol': key = srepr(arg)[7:].strip('(').strip(')').strip("'") if key not in arg_map.keys(): raise Exception('argument mapping incomplete, missing ' + key) if 'x' not in arg_map.keys(): raise Exception('argument mapping must contain "x" for the ' + 'independent variable') # Get the data data = {} args = {} for key in arg_map.keys(): data[key] = target[arg_map[key]] # Callable functions args[key] = symbols(key) r, s1, s2 = _build_func(eqn, **args) r_val = r(*data.values()) s1_val = s1(*data.values()) s2_val = s2(*data.values()) values = {'S1': s1_val, 'S2': s2_val, 'rate': r_val} return values
def listToValueList(lst, first_time): precision = trustedValuesDict["precision"] mpm.mp.dps = precision # Replace all integer fractions with the correct floating point representation: index = 0 for expr in lst: string = srepr(expr) string2 = re.sub( 'Rational\(([0-9]+), ([0-9]+)\)', "((Float('\\1'," + str(2 * precision) + "))/(Float('\\2'," + str(2 * precision) + ")))", string) string3 = re.sub( 'Rational\((-[0-9]+), ([0-9]+)\)', "((Float('\\1'," + str(2 * precision) + "))/(Float('\\2'," + str(2 * precision) + ")))", string2) newexpr = eval(string3) lst[index] = newexpr index += 1 # List all the free symbols in the expressions in [lst]. # These variables will be automatically set to random # values in the range [0,1) below. list_free_symbols = sum(lst).free_symbols # To ensure the random values are consistent for testing purposes, we will # sort the list of free symbols. This requires that we first convert # all SymPy symbols to strings, storing to list_symbol_strings, # and then we'll use zip() to sort both lists in alphabetical order, # based on the strings in the first list: list_symbol_strings = [] for var in list_free_symbols: list_symbol_strings.append(str(var)) # https://stackoverflow.com/questions/13668393/python-sorting-two-lists list_symbol_strings, list_free_symbols = (list(x) for x in zip( *sorted(zip(list_symbol_strings, list_free_symbols)))) # Set the random seed according to trustedValues.seed: random.seed(trustedValuesDict["seed"]) # Next we will write a short Python code that first declares all # of the free variables in the "everything" expression # to random values with 30 significant digits of precision. # (This is accomplished by calling random.random() to get # a 16-significant-digit random number between 0 and 1, # and then taking the 30-significant-digit square root # of that number.) stringexec = """ from sympy import Integer,Symbol,symbols,simplify,Rational,Function,srepr,sin,cos,exp,log,Abs,Add,Mul,Pow,preorder_traversal,N,Float,S,var,sympify import mpmath as mpm mpm.mp.dps = """ + str(precision) + "\n" for var in list_free_symbols: # BE CAREFUL: You must declare all variables using mpm.mpf('string')! # http://mpmath.org/doc/1.1.0/basics.html#providing-correct-input # First make sure M_PI is set to its correct value, pi, to the desired number of significant digits: if str(var) == "M_PI": stringexec += str(var) + " = mpm.pi\n" # Then make sure M_SQRT1_2 is set to its correct value, 1/sqrt(2), to the desired number of significant digits: elif str(var) == "M_SQRT1_2": stringexec += str(var) + " = mpm.mpf(1/mpm.sqrt(2))\n" # All other free variables are set to random numbers else: stringexec += str(var) + " = mpm.mpf(\'" + str( mpm.sqrt(mpm.mpf(random.random()))) + "\')\n" # Then it creates the code that evaluates the result # to 30 significant digits. stringexec += "lst = " + str(lst) # https://stackoverflow.com/questions/38817962/python-3-need-from-exec-to-return-values # Finally we execute stringexec to a local namespace "loc", and store the # result of the evaluated "everything" expression to "result". # loc = {} exec(stringexec, {}, loc) evaled_lst = loc['lst'] if first_time == True: index = 0 for result in evaled_lst: if result != 0 and mpm.fabs(result) < 100 * 10**(-precision): print("Found |result| (" + str(mpm.fabs(result)) + ") close to zero. Checking if indeed it should be zero.") # Now double the precision and redo. If number drops in magnitude loc2xprec = {} stringexec = stringexec.replace( "mpm.mp.dps = " + str(precision), "mpm.mp.dps = " + str(2 * precision)) exec(stringexec, {}, loc2xprec) evaled_lst2xprec = loc2xprec['lst'] if mpm.fabs( evaled_lst2xprec[index]) < 100 * 10**(-2 * precision): print( "After re-evaluating with twice the digits of precision, |result| dropped to " + str(evaled_lst2xprec[index]) + ". Setting value to zero") evaled_lst[index] = 0 index += 1 # Make sure that all results in evaled_lst *except* zeros are mpm.mpf type! for i in range(len(evaled_lst)): if evaled_lst[i] != 0: evaled_lst[i] = mpm.mpf(str(evaled_lst[i])) return evaled_lst
def _sympyrepr(self, printer, *args): return "RlpSum(" + str(self.query_symbols) + " in " + str(self.query) + ", " + srepr(self.expression) + ")"
#!/usr/bin/env python # -*- coding: utf-8 -*- from sympy import init_printing, Integral, latex, pretty, pprint, sqrt, symbols, srepr init_printing(use_unicode=True) x, y, z = symbols('x y z') print(Integral(sqrt(1 / x), x)) print(srepr(Integral(sqrt(1 / x), x))) pprint(Integral(sqrt(1 / x), x), use_unicode=False) print(pretty(Integral(sqrt(1 / x), x), use_unicode=False)) print(latex(Integral(sqrt(1 / x), x))) from sympy.printing.mathml import print_mathml print_mathml(Integral(sqrt(1 / x), x)) from sympy.printing.dot import dotprint from sympy.abc import x print(dotprint(x + 2))
def parsed_math(xml_expr, nsp='', func=None): if func is None: return parse_expr(pmml_to_srepr(xml_expr, nsp=nsp)) else: return parse_expr(func(pmml_to_srepr(xml_expr, nsp=nsp))) if __name__ == '__main__': import sympy as sy import lxml.etree as et expr = sy.sympify('rho*V*(H + C_p*(T_f-T_i))') # expr = sy.sympify('L*((0.75)*mu_l*mu_h + g*t + (mu_l-g)*(t-mu_e)*0.5)') print('The result should look like this: \n\n\t', sy.srepr(expr)) print('\nwhich parses to this: \n\t', parse_expr(sy.srepr(expr)), '\n----') # sy.parsing.sympy_parser.parse_expr(sy.srepr(expr)) xml_expr = """ <StaticValue> <Apply function="*"> <FieldRef field="V"/> <FieldRef field="rho"/> <Apply function="+"> <FieldRef field="H"/> <Apply function="*"> <FieldRef field="C_p"/> <Apply function="+"> <FieldRef field="T_f"/> <Apply function="*">
A_expr_dummy, A_expr_dummy_syms = sympyutils.dummify(A_expr, x_syms) A_dot_expr_dummy, A_dot_expr_dummy_syms = sympyutils.dummify( A_dot_expr, x_syms) dqdotdot_dq_expr_dummy, dqdotdot_dq_expr_dummy_syms = sympyutils.dummify( dqdotdot_dq_expr, x_and_u_syms) dqdotdot_dqdot_expr_dummy, dqdotdot_dqdot_expr_dummy_syms = sympyutils.dummify( dqdotdot_dqdot_expr, x_and_u_syms) print "Saving sympy expressions..." current_source_file_path = pathutils.get_current_source_file_path() with open( current_source_file_path + "/data/sympy/quadrotor3d_A_expr_dummy.dat", "w") as f: f.write(sympy.srepr(A_expr_dummy)) with open( current_source_file_path + "/data/sympy/quadrotor3d_A_dot_expr_dummy.dat", "w") as f: f.write(sympy.srepr(A_dot_expr_dummy)) with open( current_source_file_path + "/data/sympy/quadrotor3d_dqdotdot_dq_expr_dummy.dat", "w") as f: f.write(sympy.srepr(dqdotdot_dq_expr_dummy)) with open( current_source_file_path + "/data/sympy/quadrotor3d_dqdotdot_dqdot_expr_dummy.dat", "w") as f: f.write(sympy.srepr(dqdotdot_dqdot_expr_dummy)) with open( current_source_file_path +
def apply_z_rule_2(clause, known_expressions, verbose=False): # Example: p_1 + q_1 - 2*z_1_2 = 0 # p1 and z_1_2 must be equal to q1, otherwise the equation can't be satisfied. # For more examples please refer to tests. new_known_expressions = {} even_positive_terms = [] even_negative_terms = [] odd_terms = [] if clause.func == Add: for term in clause.args: if term.func == Symbol: odd_terms.append(term) if isinstance(term, Number): if term % 2 == 0 and term > 0: even_positive_terms.append(term) elif term % 2 == 0 and term < 0: even_negative_terms.append(term) else: odd_terms.append(term) if term.func == Mul: first_argument = term.args[0] if isinstance(first_argument, Number): if first_argument % 2 == 0 and first_argument > 0: even_positive_terms.append(term) elif first_argument % 2 == 0 and first_argument < 0: even_negative_terms.append(term) else: odd_terms.append(term) else: odd_terms.append(term) if len(odd_terms) == 1: if type(odd_terms[0]) == Symbol: new_known_expressions[odd_terms[0]] = 0 elif type(odd_terms[0]) == Mul: term = odd_terms[0] if isinstance(term.args[0], Number): term = term / term.args[0] new_known_expressions[term] = 0 else: print("TODO: Z rule 2: don't know this type!") pdb.set_trace() if len(odd_terms) == 2: non_number_index = None if isinstance(odd_terms[0], Number): non_number_index = 1 elif isinstance(odd_terms[1], Number): non_number_index = 0 if non_number_index is not None: term = odd_terms[non_number_index] if type(term) == Symbol: new_known_expressions[term] = 1 elif type(term) == Mul: for arg in term.args: if not isinstance(arg, Number): new_known_expressions[arg] = 1 else: # TODO: Example of clause which results in this case: # 2*q_2 + z_4_6 + z_5_6 - 4 # (p=23, q=23, m=529) # This should be handled by rule 5 # print("TODO: Z rule 2: don't know this type!") # pdb.set_trace() pass else: if 'q' in str(odd_terms[0]): non_q_index = 1 else: non_q_index = 0 variable_0 = odd_terms[1 - non_q_index] variable_1 = odd_terms[non_q_index] if type(variable_0) == Mul: if isinstance(variable_0.args[0], Number): variable_0 = variable_0 / variable_0.args[0] if type(variable_1) == Mul: if isinstance(variable_1.args[0], Number): variable_1 = variable_1 / variable_1.args[0] new_known_expressions[variable_1] = variable_0 if len(even_negative_terms) == 1: term = even_negative_terms[0] if isinstance(term, Number): # TODO: Example of clause which results in this case: # q_2 + z_5_6 + 2*z_7_8 - 2 # (p=29, q=23, m=667) # pdb.set_trace() pass elif type(term) == Mul: if len(even_positive_terms) == 0: term = term / term.args[0] new_known_expressions[term] = variable_0 else: pass # pdb.set_trace() else: print("TODO: Z rule 2: don't know this type!") pdb.set_trace() if len(odd_terms) == 3: number_index = None if isinstance(odd_terms[0], Number): number_index = 0 elif isinstance(odd_terms[1], Number): number_index = 1 elif isinstance(odd_terms[2], Number): number_index = 2 if number_index is not None: indices = [0, 1, 2] indices.remove(number_index) new_term = odd_terms[indices[0]] * odd_terms[indices[1]] if isinstance(new_term.args[0], Number): new_term = new_term / new_term.args[0] if 'Pow' in srepr(new_term): new_term = simplify_clause(new_term, {}) new_known_expressions[new_term] = 0 if len(new_known_expressions) != 0: known_expressions = {**known_expressions, **new_known_expressions} if verbose: print("Z rule 2 applied:", new_known_expressions) return known_expressions
[ omega_x_terms_expr[theta_dot_expr], omega_x_terms_expr[psi_dot_expr], omega_x_terms_expr[phi_dot_expr] ] ] ) A_dot_expr = sympy.trigsimp(A_expr.diff(t_expr)) syms = [ theta_expr, psi_expr, phi_expr, theta_dot_expr, psi_dot_expr, phi_dot_expr ] print "Dummifying sympy expressions..." A_expr_dummy, A_expr_dummy_syms = sympyutils.dummify( A_expr, syms ) A_dot_expr_dummy, A_dot_expr_dummy_syms = sympyutils.dummify( A_dot_expr, syms ) print "Saving sympy expressions..." current_source_file_path = pathutils.get_current_source_file_path() with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy.dat", "w") as f: f.write(sympy.srepr(A_expr_dummy)) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_dot_expr_dummy.dat", "w") as f: f.write(sympy.srepr(A_dot_expr_dummy)) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy_syms.dat", "w") as f: f.write(sympy.srepr(sympy.Matrix(A_expr_dummy_syms))) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_dot_expr_dummy_syms.dat", "w") as f: f.write(sympy.srepr(sympy.Matrix(A_dot_expr_dummy_syms))) else: print "Loading sympy expressions..." current_source_file_path = pathutils.get_current_source_file_path() with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy.dat", "r") as f: A_expr_dummy = sympy.sympify(f.read()) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_dot_expr_dummy.dat", "r") as f: A_dot_expr_dummy = sympy.sympify(f.read()) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy_syms.dat", "r") as f: A_expr_dummy_syms = array(sympy.sympify(f.read())).squeeze()
def expr_convert_to_SIMD_intrins(expr, map_sym_to_rat=None, prefix="", SIMD_find_more_FMAsFMSs="True", debug="False"): """ Convert expression to SIMD compiler intrinsics :arg: SymPy expression :arg: symbol to rational dictionary :arg: option to find more FMA/FMS patterns :arg: back-substitute and check difference :return: expression containing SIMD compiler intrinsics >>> from sympy.abc import a, b, c, d >>> from cse_helpers import cse_preprocess >>> convert = expr_convert_to_SIMD_intrins >>> convert(a**2) MulSIMD(a, a) >>> convert(a**(-2)) DivSIMD(_Integer_1, MulSIMD(a, a)) >>> convert(a**(1/2)) SqrtSIMD(a) >>> convert(a**(-1/2)) DivSIMD(1, SqrtSIMD(a)) >>> from sympy import Rational >>> convert(a**Rational(1, 3)) CbrtSIMD(a) >>> convert(a**b) PowSIMD(a, b) >>> convert(a - b) SubSIMD(a, b) >>> convert(a + b - c) AddSIMD(b, SubSIMD(a, c)) >>> convert(a + b + c) AddSIMD(a, AddSIMD(b, c)) >>> convert(a + b + c + d) AddSIMD(AddSIMD(a, b), AddSIMD(c, d)) >>> convert(a*b*c) MulSIMD(a, MulSIMD(b, c)) >>> convert(a*b*c*d) MulSIMD(MulSIMD(a, b), MulSIMD(c, d)) >>> convert(a/b) DivSIMD(a, b) >>> convert(a*b + c) FusedMulAddSIMD(a, b, c) >>> convert(a*b - c) FusedMulSubSIMD(a, b, c) >>> convert(-a*b + c) NegFusedMulAddSIMD(a, b, c) >>> convert(-a*b - c) NegFusedMulSubSIMD(a, b, c) """ for item in preorder_traversal(expr): for arg in item.args: if isinstance(arg, Symbol): var(str(arg)) def lookup_rational(arg): if arg.func == Symbol: try: arg = map_sym_to_rat[arg] except KeyError: pass return arg if map_sym_to_rat is None: expr, map_sym_to_rat = cse_preprocess(expr) map_rat_to_sym = {map_sym_to_rat[v]: v for v in map_sym_to_rat} expr_orig, tree = expr, ExprTree(expr) AbsSIMD = Function("AbsSIMD") AddSIMD = Function("AddSIMD") SubSIMD = Function("SubSIMD") MulSIMD = Function("MulSIMD") FusedMulAddSIMD = Function("FusedMulAddSIMD") FusedMulSubSIMD = Function("FusedMulSubSIMD") NegFusedMulAddSIMD = Function("NegFusedMulAddSIMD") NegFusedMulSubSIMD = Function("NegFusedMulSubSIMD") DivSIMD = Function("DivSIMD") SignSIMD = Function("SignSIMD") PowSIMD = Function("PowSIMD") SqrtSIMD = Function("SqrtSIMD") CbrtSIMD = Function("CbrtSIMD") ExpSIMD = Function("ExpSIMD") LogSIMD = Function("LogSIMD") SinSIMD = Function("SinSIMD") CosSIMD = Function("CosSIMD") # Step 1: Replace transcendental functions, power functions, and division expressions. # Note: SymPy does not represent fractional integers as rationals since # those are explicitly declared using the rational class, and hence # the following algorithm does not affect fractional integers. # SymPy: srepr(a**(-2)) = Pow(a, -2) # NRPy: srepr(a**(-2)) = DivSIMD(1, MulSIMD(a, a)) for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args if func == Abs: subtree.expr = AbsSIMD(args[0]) elif func == exp: subtree.expr = ExpSIMD(args[0]) elif func == log: subtree.expr = LogSIMD(args[0]) elif func == sin: subtree.expr = SinSIMD(args[0]) elif func == cos: subtree.expr = CosSIMD(args[0]) elif func == sign: subtree.expr = SignSIMD(args[0]) tree.reconstruct() def IntegerPowSIMD(a, n): # Recursive Helper Function: Construct Integer Powers if n == 2: return MulSIMD(a, a) if n > 2: return MulSIMD(IntegerPowSIMD(a, n - 1), a) if n <= -2: one = Symbol(prefix + '_Integer_1') try: map_rat_to_sym[1] except KeyError: map_sym_to_rat[one], map_rat_to_sym[1] = S.One, one return DivSIMD(one, IntegerPowSIMD(a, -n)) if n == -1: one = Symbol(prefix + '_Integer_1') try: map_rat_to_sym[1] except KeyError: map_sym_to_rat[one], map_rat_to_sym[1] = S.One, one return DivSIMD(one, a) for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args if func == Pow: exponent = lookup_rational(args[1]) if exponent == 0.5: subtree.expr = SqrtSIMD(args[0]) subtree.children.pop(1) elif exponent == -0.5: subtree.expr = DivSIMD(1, SqrtSIMD(args[0])) tree.build(subtree) elif exponent == Rational(1, 3): subtree.expr = CbrtSIMD(args[0]) subtree.children.pop(1) elif isinstance(exponent, Integer): subtree.expr = IntegerPowSIMD(args[0], exponent) tree.build(subtree) else: subtree.expr = PowSIMD(*args) tree.reconstruct() # Step 2: Replace subtraction expressions. # Note: SymPy: srepr(a - b) = Add(a, Mul(-1, b)) # NRPy: srepr(a - b) = SubSIMD(a, b) for subtree in tree.preorder(): func = subtree.expr.func args = list(subtree.expr.args) if func == Add: try: # Find the first occurrence of a negative product inside the addition i = next(i for i, arg in enumerate(args) if arg.func == Mul and \ any(lookup_rational(arg) == -1 for arg in args[i].args)) # Find the first occurrence of a negative symbol inside the product j = next(j for j, arg in enumerate(args[i].args) if lookup_rational(arg) == -1) # Find the first non-negative argument of the product k = next(k for k in range(len(args)) if k != i) # Remove the negative symbol from the product subargs = list(args[i].args) subargs.pop(j) # Build the subtraction expression for replacement subexpr = SubSIMD(args[k], Mul(*subargs)) args = [arg for arg in args if arg not in (args[i], args[k])] if len(args) > 0: subexpr = Add(subexpr, *args) subtree.expr = subexpr tree.build(subtree) except StopIteration: pass tree.reconstruct() # Step 3: Replace addition and multiplication expressions. # Note: SIMD addition and multiplication compiler intrinsics can read # only two arguments at once, whereas SymPy's Mul() and Add() # operators can read an arbitrary number of arguments. # SymPy: srepr(a*b*c*d) = Mul(a, b, c, d) # NRPy: srepr(a*b*c*d) = MulSIMD(MulSIMD(a, b), MulSIMD(c, d)) for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args if func in (Mul, Add): func = MulSIMD if func == Mul else AddSIMD subexpr = func(*args[-2:]) args, N = args[:-2], len(args) - 2 for i in range(0, N, 2): if N - i > 1: tmpexpr = func(args[i], args[i + 1]) subexpr = func(tmpexpr, subexpr, evaluate=False) else: subexpr = func(args[i], subexpr, evaluate=False) subtree.expr = subexpr tree.build(subtree) tree.reconstruct() # Step 4: Replace the pattern Mul(Div(1, b), a) or Mul(a, Div(1, b)) with Div(a, b). for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # MulSIMD(DivSIMD(1, b), a) >> DivSIMD(a, b) if func == MulSIMD and args[0].func == DivSIMD and \ lookup_rational(args[0].args[0]) == 1: subtree.expr = DivSIMD(args[1], args[0].args[1]) tree.build(subtree) # MulSIMD(a, DivSIMD(1, b)) >> DivSIMD(a, b) elif func == MulSIMD and args[1].func == DivSIMD and \ lookup_rational(args[1].args[0]) == 1: subtree.expr = DivSIMD(args[0], args[1].args[1]) tree.build(subtree) tree.reconstruct() # Step 5: Now that all multiplication and addition functions only take two # arguments, we can define fused-multiply-add functions, # where AddSIMD(a, MulSIMD(b, c)) = b*c + a = FusedMulAddSIMD(b, c, a), # or AddSIMD(MulSIMD(b, c), a) = b*c + a = FusedMulAddSIMD(b, c, a). # Note: Fused-multiply-add (FMA3) is standard on Intel CPUs with the AVX2 # instruction set, starting with Haswell processors in 2013: # https://en.wikipedia.org/wiki/Haswell_(microarchitecture) # Step 5.a: Find double FMA patterns first [e.g. FMA(a, b, FMA(c, d, e))]. # Note: Double FMA simplifications do not guarantee a significant performance impact when solving BSSN equations if SIMD_find_more_FMAsFMSs == "True": for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # a + b*c + d*e -> FMA(b,c,FMA(d,e,a)) # AddSIMD(a, AddSIMD(MulSIMD(b,c), MulSIMD(d,e))) >> FusedMulAddSIMD(b, c, FusedMulAddSIMD(d,e,a)) # Validate: # x = a + b*c + d*e # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") if (func == AddSIMD and args[1].func == AddSIMD and args[1].args[0].func == MulSIMD and args[1].args[1].func == MulSIMD): subtree.expr = FusedMulAddSIMD( args[1].args[0].args[0], args[1].args[0].args[1], FusedMulAddSIMD(args[1].args[1].args[0], args[1].args[1].args[1], args[0])) tree.build(subtree) # b*c + d*e + a -> FMA(b,c,FMA(d,e,a)) # Validate: # x = b*c + d*e + a # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") # AddSIMD(AddSIMD(MulSIMD(b,c), MulSIMD(d,e)),a) >> FusedMulAddSIMD(b, c, FusedMulAddSIMD(d,e,a)) elif func == AddSIMD and args[0].func == AddSIMD and args[0].args[ 0].func == MulSIMD and args[0].args[1].func == MulSIMD: subtree.expr = FusedMulAddSIMD( args[0].args[0].args[0], args[0].args[0].args[1], FusedMulAddSIMD(args[0].args[1].args[0], args[0].args[1].args[1], args[1])) tree.build(subtree) tree.reconstruct() # Step 5.b: Find single FMA patterns. for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # AddSIMD(MulSIMD(b, c), a) >> FusedMulAddSIMD(b, c, a) if func == AddSIMD and args[0].func == MulSIMD: subtree.expr = FusedMulAddSIMD(args[0].args[0], args[0].args[1], args[1]) tree.build(subtree) # AddSIMD(a, MulSIMD(b, c)) >> FusedMulAddSIMD(b, c, a) elif func == AddSIMD and args[1].func == MulSIMD: subtree.expr = FusedMulAddSIMD(args[1].args[0], args[1].args[1], args[0]) tree.build(subtree) # SubSIMD(MulSIMD(b, c), a) >> FusedMulSubSIMD(b, c, a) elif func == SubSIMD and args[0].func == MulSIMD: subtree.expr = FusedMulSubSIMD(args[0].args[0], args[0].args[1], args[1]) tree.build(subtree) # SubSIMD(a, MulSIMD(b, c)) >> NegativeFusedMulAddSIMD(b, c, a) elif func == SubSIMD and args[1].func == MulSIMD: subtree.expr = NegFusedMulAddSIMD(args[1].args[0], args[1].args[1], args[0]) tree.build(subtree) # FMS(-1, MulSIMD(a, b), c) >> NegativeFusedMulSubSIMD(b, c, a) func = subtree.expr.func args = subtree.expr.args if func == FusedMulSubSIMD and args[ 1].func == MulSIMD and lookup_rational(args[0]) == -1: subtree.expr = NegFusedMulSubSIMD(args[1].args[0], args[1].args[1], args[2]) tree.build(subtree) tree.reconstruct() # Step 5.c: Remaining double FMA patterns that previously in Step 5.a were difficult to find. # Note: Double FMA simplifications do not guarantee a significant performance impact when solving BSSN equations if SIMD_find_more_FMAsFMSs == "True": for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # (b*c - d*e) + a -> AddSIMD(a, FusedMulSubSIMD(b, c, MulSIMD(d, e))) >> FusedMulSubSIMD(b, c, FusedMulSubSIMD(d,e,a)) # Validate: # x = (b*c - d*e) + a # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") if func == AddSIMD and args[1].func == FusedMulSubSIMD and args[ 1].args[2].func == MulSIMD: subtree.expr = FusedMulSubSIMD( args[1].args[0], args[1].args[1], FusedMulSubSIMD(args[1].args[2].args[0], args[1].args[2].args[1], args[0])) tree.build(subtree) # b*c - (a - d*e) -> SubSIMD(FusedMulAddSIMD(b, c, MulSIMD(d, e)), a) >> FMA(b,c,FMS(d,e,a)) # Validate: # x = b * c - (a - d * e) # outputC(x, "x", params="SIMD_enable=True,SIMD_debug=True") elif func == SubSIMD and args[0].func == FusedMulAddSIMD and args[ 0].args[2].func == MulSIMD: subtree.expr = FusedMulAddSIMD( args[0].args[0], args[0].args[1], FusedMulSubSIMD(args[0].args[2].args[0], args[0].args[2].args[1], args[1])) tree.build(subtree) # (b*c - d*e) - a -> SubSIMD(FusedMulSubSIMD(b, c, MulSIMD(d, e)), a) >> FMS(b,c,FMA(d,e,a)) # Validate: # x = (b*c - d*e) - a # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") elif func == SubSIMD and args[0].func == FusedMulSubSIMD and args[ 0].args[2].func == MulSIMD: subtree.expr = FusedMulSubSIMD( args[0].args[0], args[0].args[1], FusedMulAddSIMD(args[0].args[2].args[0], args[0].args[2].args[1], args[1])) tree.build(subtree) tree.reconstruct() # Step 5.d: NegFusedMulAddSIMD(a,b,c) = -a*b + c: for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # FMA(a,Mul(-1,b),c) >> NFMA(a,b,c) if func == FusedMulAddSIMD and args[1].func == MulSIMD and \ lookup_rational(args[1].args[0]) == -1: subtree.expr = NegFusedMulAddSIMD(args[0], args[1].args[1], args[2]) tree.build(subtree) # FMA(a,Mul(b,-1),c) >> NFMA(a,b,c) elif func == FusedMulAddSIMD and args[1].func == MulSIMD and \ lookup_rational(args[1].args[1]) == -1: subtree.expr = NegFusedMulAddSIMD(args[0], args[1].args[0], args[2]) tree.build(subtree) # FMA(Mul(-1,a), b,c) >> NFMA(a,b,c) elif func == FusedMulAddSIMD and args[0].func == MulSIMD and \ lookup_rational(args[0].args[0]) == -1: subtree.expr = NegFusedMulAddSIMD(args[0].args[1], args[1], args[2]) tree.build(subtree) # FMA(Mul(a,-1), b,c) >> NFMA(a,b,c) elif func == FusedMulAddSIMD and args[0].func == MulSIMD and \ lookup_rational(args[0].args[1]) == -1: subtree.expr = NegFusedMulAddSIMD(args[0].args[0], args[1], args[2]) tree.build(subtree) tree.reconstruct() # Step 5.e: Replace e.g., FMA(-1,b,c) with SubSIMD(c,b) and similar patterns for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # FMA(-1,b,c) >> SubSIMD(c,b) if func == FusedMulAddSIMD and lookup_rational(args[0]) == -1: subtree.expr = SubSIMD(args[2], args[1]) tree.build(subtree) # FMA(a,-1,c) >> SubSIMD(c,a) elif func == FusedMulAddSIMD and lookup_rational(args[1]) == -1: subtree.expr = SubSIMD(args[2], args[0]) tree.build(subtree) # FMS(a,-1,c) >> MulSIMD(-1,AddSIMD(a,c)) elif func == FusedMulSubSIMD and lookup_rational(args[1]) == -1: subtree.expr = MulSIMD(args[1], AddSIMD(args[0], args[2])) tree.build(subtree) # FMS(-1,b,c) >> MulSIMD(-1,AddSIMD(b,c)) elif func == FusedMulSubSIMD and lookup_rational(args[0]) == -1: subtree.expr = MulSIMD(args[0], AddSIMD(args[1], args[2])) tree.build(subtree) tree.reconstruct() # Step 5.f: NegFusedMulSubSIMD(a,b,c) = -a*b - c: for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # NFMA(a,b,Mul(-1,c)) >> NFMS(a,b,c) if func == NegFusedMulAddSIMD and args[2].func == MulSIMD and \ lookup_rational(args[2].args[0]) == -1: subtree.expr = NegFusedMulSubSIMD(args[0], args[1], args[2].args[1]) tree.build(subtree) # NFMA(a,b,Mul(c,-1)) >> NFMS(a,b,c) elif func == NegFusedMulAddSIMD and args[2].func == MulSIMD and \ lookup_rational(args[2].args[1]) == -1: subtree.expr = NegFusedMulSubSIMD(args[0], args[1], args[2].args[0]) tree.build(subtree) # FMS(a,Mul(-1,b),c) >> NFMS(a,b,c) elif func == FusedMulSubSIMD and args[1].func == MulSIMD and \ lookup_rational(args[1].args[0]) == -1: subtree.expr = NegFusedMulSubSIMD(args[0], args[1].args[1], args[2]) tree.build(subtree) # FMS(a,Mul(b,-1),c) >> NFMS(a,b,c) elif func == FusedMulSubSIMD and args[1].func == MulSIMD and \ lookup_rational(args[1].args[1]) == -1: subtree.expr = NegFusedMulSubSIMD(args[0], args[1].args[0], args[2]) tree.build(subtree) # FMS(a,Mul([something],Mul(-1,b)),c) >> NFMS(a,Mul([something],b),c) elif func == FusedMulSubSIMD and args[1].func == MulSIMD and \ args[1].args[1].func == MulSIMD and lookup_rational(args[1].args[1].args[0]) == -1: subtree.expr = NegFusedMulSubSIMD( args[0], MulSIMD(args[1].args[0], args[1].args[1].args[1]), args[2]) tree.build(subtree) # FMS(a,Mul([something],Mul(b,-1)),c) >> NFMS(a,Mul([something],b),c) elif func == FusedMulSubSIMD and args[1].func == MulSIMD and \ args[1].args[1].func == MulSIMD and lookup_rational(args[1].args[1].args[1]) == -1: subtree.expr = NegFusedMulSubSIMD( args[0], MulSIMD(args[1].args[0], args[1].args[1].args[0]), args[2]) tree.build(subtree) tree.reconstruct() # Step 5.g: Find single FMA patterns again, as some new ones might be found. for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # AddSIMD(MulSIMD(b, c), a) >> FusedMulAddSIMD(b, c, a) if func == AddSIMD and args[0].func == MulSIMD: subtree.expr = FusedMulAddSIMD(args[0].args[0], args[0].args[1], args[1]) tree.build(subtree) # AddSIMD(a, MulSIMD(b, c)) >> FusedMulAddSIMD(b, c, a) elif func == AddSIMD and args[1].func == MulSIMD: subtree.expr = FusedMulAddSIMD(args[1].args[0], args[1].args[1], args[0]) tree.build(subtree) # SubSIMD(MulSIMD(b, c), a) >> FusedMulSubSIMD(b, c, a) elif func == SubSIMD and args[0].func == MulSIMD: subtree.expr = FusedMulSubSIMD(args[0].args[0], args[0].args[1], args[1]) tree.build(subtree) expr = tree.reconstruct() if debug == "True": expr_check = eval(str(expr).replace("SIMD", "SIMD_check")) expr_check = expr_check.subs(-1, Symbol('_NegativeOne_')) expr_diff = expr_check - expr_orig # The eval(str(srepr())) below normalizes the expression, # fixing a cancellation issue in SymPy ~0.7.4. expr_diff = eval(str(srepr(expr_diff))) tree_diff = ExprTree(expr_diff) for subtree in tree_diff.preorder(): subexpr = subtree.expr if subexpr.func == Float: if abs(subexpr - Integer(subexpr)) < 1.0e-14 * subexpr: subtree.expr = Integer(subexpr) expr_diff = tree_diff.reconstruct() if expr_diff != 0: simp_expr_diff = simplify(expr_diff) if simp_expr_diff != 0: raise Warning('Expression Difference: ' + str(simp_expr_diff)) return (expr)
def test_issue1689(): assert srepr(S(1.0 + 0J)) == srepr(S(1.0)) == srepr(Float(1.0))
def apply_parity_rule(clause, known_expressions, verbose=False): """ Extends known_expressions by applying parity rule (see example below). Example: p_1 + q_1 - 2*z_1_2 = 0 p1 and z_1_2 must be equal to q1, otherwise the equation can't be satisfied. For more examples please refer to the tests. This rule turned out to be the most problematic one. There are known cases it doesn't solve (see comments) and it might require refactoring/reworking. Args: clause: sympy expression representing a clause. known_expressions (dict): See module documentation at the top. verbose (bool): See module documentation at the top. Returns: known_expressions (dict): See module documentation at the top. """ new_known_expressions = {} even_positive_terms = [] even_negative_terms = [] odd_terms = [] if clause.func == Add: for term in clause.args: if term.func == Symbol: odd_terms.append(term) if isinstance(term, Number): if term % 2 == 0 and term > 0: even_positive_terms.append(term) elif term % 2 == 0 and term < 0: even_negative_terms.append(term) else: odd_terms.append(term) if term.func == Mul: first_argument = term.args[0] if isinstance(first_argument, Number): if first_argument % 2 == 0 and first_argument > 0: even_positive_terms.append(term) elif first_argument % 2 == 0 and first_argument < 0: even_negative_terms.append(term) else: odd_terms.append(term) else: odd_terms.append(term) if len(odd_terms) == 1: if type(odd_terms[0]) == Symbol: new_known_expressions[odd_terms[0]] = 0 elif type(odd_terms[0]) == Mul: term = odd_terms[0] if isinstance(term.args[0], Number): term = term / term.args[0] new_known_expressions[term] = 0 else: print("TODO: Z rule 2: don't know this type!") pdb.set_trace() if len(odd_terms) == 2: non_number_index = None if isinstance(odd_terms[0], Number): non_number_index = 1 elif isinstance(odd_terms[1], Number): non_number_index = 0 if non_number_index is not None: term = odd_terms[non_number_index] if type(term) == Symbol: new_known_expressions[term] = 1 elif type(term) == Mul: for arg in term.args: if not isinstance(arg, Number): new_known_expressions[arg] = 1 else: # TODO: Example of clause which results in this case: # 2*q_2 + z_4_6 + z_5_6 - 4 # (p=23, q=23, m=529) # This should be handled by rule 5 # print("TODO: Z rule 2: don't know this type!") # pdb.set_trace() pass else: if 'q' in str(odd_terms[0]): non_q_index = 1 else: non_q_index = 0 variable_0 = odd_terms[1 - non_q_index] variable_1 = odd_terms[non_q_index] if type(variable_0) == Mul: if isinstance(variable_0.args[0], Number): variable_0 = variable_0 / variable_0.args[0] if type(variable_1) == Mul: if isinstance(variable_1.args[0], Number): variable_1 = variable_1 / variable_1.args[0] new_known_expressions[variable_1] = variable_0 if len(even_negative_terms) == 1: term = even_negative_terms[0] if isinstance(term, Number): # TODO: Example of clause which results in this case: # q_2 + z_5_6 + 2*z_7_8 - 2 # (p=29, q=23, m=667) # pdb.set_trace() pass elif type(term) == Mul: if len(even_positive_terms) == 0: term = term / term.args[0] new_known_expressions[term] = variable_0 else: pass # pdb.set_trace() else: print("TODO: Z rule 2: don't know this type!") pdb.set_trace() if len(odd_terms) == 3: number_index = None if isinstance(odd_terms[0], Number): number_index = 0 elif isinstance(odd_terms[1], Number): number_index = 1 elif isinstance(odd_terms[2], Number): number_index = 2 if number_index is not None: indices = [0, 1, 2] indices.remove(number_index) new_term = odd_terms[indices[0]] * odd_terms[indices[1]] if isinstance(new_term.args[0], Number): new_term = new_term / new_term.args[0] if 'Pow' in srepr(new_term): new_term = simplify_clause(new_term, {}) new_known_expressions[new_term] = 0 if len(new_known_expressions) != 0: known_expressions = {**known_expressions, **new_known_expressions} if verbose: print("Z rule 2 applied:", new_known_expressions) return known_expressions
def exprstrout(key, expr): return srepr(expr) + " " + ErrorOutput.messages.get(key, None)
# tt = np.random.randint(2, size=8, dtype=bool) tt = np.array([False, True, True, True, False, False, True, True]) print(tt.tolist()) print(tt.tolist(), file=logfile) g = GP( num_vars=int(np.log2(tt.shape[0])), unique_pop=opt.unique_pop, bin_ops=(sympy.And, sympy.Or), target_tt=tt, pop_size=opt.pop_size, size_next_gen=opt.size_next_gen, lucky_per=opt.lucky_per, weight_num_gates=opt.weight_num_gates, weight_num_agree=opt.weight_num_agree, add_naive=opt.add_naive, ) fn = g.util.syms[0] | (g.util.syms[1] & (~g.util.syms[2] | g.util.syms[0])) srepr = sympy.srepr(fn) mt = tt_to_sympy_minterms(g.util.target_tt) sop_form = SOPform(g.util.syms, mt) try: g.run(num_generations=opt.num_generations, init_pop_size=opt.init_pop_size) # g.run() except KeyboardInterrupt: pass g.print_best(logfile) # g.util.pool.terminate() # terminated on this process's death, or so it seems
def _getDiffList(self): """ Set the list for each of the occurences of Derivative objects that appear in the equations of the model :return: List of derivatives :rtype list(str) diff_list: """ diff_list = [] for eqi in self._equation_groups['differential']: eq = eqi._getSymbolicObject('residual','rhs') _ = [diff_list.append(abs(i).args[0]) for i in eq.args if 'Derivative' in sp.srepr(i)] return diff_list
def test_hilbert_space(): hs = HilbertSpace() assert isinstance(hs, HilbertSpace) assert sstr(hs) == 'H' assert srepr(hs) == 'HilbertSpace()'
def get_sqrt_g(self): """Return square root of determinant of covariant metric tensor""" g = self.get_g() return sp.simplify(sp.srepr(sp.sqrt(g)).replace('Abs', ''))
def test_issue_4788(): assert srepr(S(1.0 + 0J)) == srepr(S(1.0)) == srepr(Float(1.0))
def test_issue1689(): assert srepr(S(1.0 + 0j)) == srepr(S(1.0)) == srepr(Real(1.0)) assert srepr(Real(1)) != srepr(Real(1.0))
[ omega_x_terms_expr[theta_dot_expr], omega_x_terms_expr[psi_dot_expr], omega_x_terms_expr[phi_dot_expr] ] ] ) A_dot_expr = sympy.trigsimp(A_expr.diff(t_expr)) syms = [ theta_expr, psi_expr, phi_expr, theta_dot_expr, psi_dot_expr, phi_dot_expr ] print("Dummifying sympy expressions...") A_expr_dummy, A_expr_dummy_syms = sympyutils.dummify( A_expr, syms ) A_dot_expr_dummy, A_dot_expr_dummy_syms = sympyutils.dummify( A_dot_expr, syms ) print( "Saving sympy expressions...") current_source_file_path = pathutils.get_current_source_file_path() with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy.dat", "w") as f: f.write(sympy.srepr(A_expr_dummy)) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_dot_expr_dummy.dat", "w") as f: f.write(sympy.srepr(A_dot_expr_dummy)) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy_syms.dat", "w") as f: f.write(sympy.srepr(sympy.Matrix(A_expr_dummy_syms))) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_dot_expr_dummy_syms.dat", "w") as f: f.write(sympy.srepr(sympy.Matrix(A_dot_expr_dummy_syms))) else: print("Loading sympy expressions...") current_source_file_path = pathutils.get_current_source_file_path() with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy.dat", "r") as f: A_expr_dummy = sympy.sympify(f.read()) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_dot_expr_dummy.dat", "r") as f: A_dot_expr_dummy = sympy.sympify(f.read()) with open(current_source_file_path+"/data/sympy/quadrotorcamera3d_A_expr_dummy_syms.dat", "r") as f: A_expr_dummy_syms = array(sympy.sympify(f.read())).squeeze()
def expr_convert_to_SIMD_intrins(expr, map_sym_to_rat, prefix="", SIMD_find_more_FMAsFMSs="False", debug="False"): # OVERRIDE; THIS NEW DEFAULT IS FASTER SIMD_find_more_FMAsFMSs = "True" for item in preorder_traversal(expr): for arg in item.args: if isinstance(arg, Symbol): var(str(arg)) def lookup_rational(arg): if arg.func == Symbol: try: arg = map_sym_to_rat[arg] except KeyError: pass return arg map_rat_to_sym = {map_sym_to_rat[v]: v for v in map_sym_to_rat} expr_orig, tree = expr, ExprTree(expr) AbsSIMD = Function("AbsSIMD") AddSIMD = Function("AddSIMD") SubSIMD = Function("SubSIMD") MulSIMD = Function("MulSIMD") FusedMulAddSIMD = Function("FusedMulAddSIMD") FusedMulSubSIMD = Function("FusedMulSubSIMD") DivSIMD = Function("DivSIMD") SignSIMD = Function("SignSIMD") PowSIMD = Function("PowSIMD") SqrtSIMD = Function("SqrtSIMD") CbrtSIMD = Function("CbrtSIMD") ExpSIMD = Function("ExpSIMD") LogSIMD = Function("LogSIMD") SinSIMD = Function("SinSIMD") CosSIMD = Function("CosSIMD") # Step 1: Replace transcendental, power, and division functions with SIMD equivalents # Note that due to how SymPy expresses rational numbers, the following does not # affect fractional expressions of integers for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args if func == Abs: subtree.expr = AbsSIMD(args[0]) elif func == exp: subtree.expr = ExpSIMD(args[0]) elif func == log: subtree.expr = LogSIMD(args[0]) elif func == sin: subtree.expr = SinSIMD(args[0]) elif func == cos: subtree.expr = CosSIMD(args[0]) elif func == sign: subtree.expr = SignSIMD(args[0]) expr = tree.reconstruct(evaluate=True) # Fun little recursive function for constructing integer powers: def IntegerPowSIMD(a, n): if n == 2: return MulSIMD(a, a) elif n > 2: return MulSIMD(IntegerPowSIMD(a, n - 1), a) elif n <= -2: one = Symbol(prefix + '_Integer_1') try: map_rat_to_sym[1] except KeyError: map_sym_to_rat[one], map_rat_to_sym[1] = S.One, one return DivSIMD(one, IntegerPowSIMD(a, -n)) elif n == -1: one = Symbol(prefix + '_Integer_1') try: map_rat_to_sym[1] except KeyError: map_sym_to_rat[one], map_rat_to_sym[1] = S.One, one return DivSIMD(one, a) for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args if func == Pow: exponent = lookup_rational(args[1]) if exponent == 0.5: subtree.expr = SqrtSIMD(args[0]) subtree.children.pop(1) # Remove 0.5 elif exponent == -0.5: subtree.expr = DivSIMD(1, SqrtSIMD(args[0])) tree.build(subtree, clear=True) elif exponent == Rational(1, 3): subtree.expr = CbrtSIMD(args[0]) subtree.children.pop(1) # Remove -0.5 elif isinstance(exponent, Integer): subtree.expr = IntegerPowSIMD(args[0], exponent) tree.build(subtree, clear=True) else: subtree.expr = PowSIMD(*args) expr = tree.reconstruct() # We must evaluate the expression, otherwise nested multiplications # will arise that conflict with the following replacements in Step 3. # Step 2: SIMD multiplication and addition compiler intrinsics read in # only two arguments at once, where SymPy's Mul() and Add() # operators can read an arbitrary number of arguments. # Here, we split e.g., Mul(a, b, c, d) into # MulSIMD(a, MulSIMD(b, MulSIMD(c, d))), # To accomplish this easily, we construct a string # 'MulSIMD(A, MulSIMD(B, ...', where MulSIMD(a, b) is some user- # defined function that takes in only two arguments, and then # evaluate the string using the eval() function. # Implementation detail: If we did not perform Step 2 above, the eval # function would automatically evaluate all Rational expressions # as though they were input as integers: e.g., 1/2 evaluates to 0. # This is undesirable, so we instead define new, temporary # functions IntegerTMP and RationalTMP that are undisturbed by # the eval() for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args if (func == Mul or func == Add): func = MulSIMD if func == Mul else AddSIMD subexpr = func(*args[-2:]) for arg in args[:-2]: subexpr = func(arg, subexpr, evaluate=False) subtree.expr = subexpr tree.build(subtree, clear=True) expr = tree.reconstruct() # Step 3: Simplification patterns: # Step 3.a: Replace the pattern Mul(Div(1, b), a) or Mul(a, Div(1, b)) with Div(a, b): for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # MulSIMD(DivSIMD(1, b), a) >> DivSIMD(a, b) if func == MulSIMD and args[0].func == DivSIMD and \ lookup_rational(args[0].args[0]) == 1: subtree.expr = DivSIMD(args[1], args[0].args[1]) tree.build(subtree, clear=True) # MulSIMD(a, DivSIMD(1, b)) >> DivSIMD(a, b) elif func == MulSIMD and args[1].func == DivSIMD and \ lookup_rational(args[1].args[0]) == 1: subtree.expr = DivSIMD(args[0], args[1].args[1]) tree.build(subtree, clear=True) expr = tree.reconstruct() # Step 3.b: Subtraction intrinsics. SymPy replaces all a - b with a + (-b) = Add(a, Mul(-1, b)) # Here, we replace # a) AddSIMD(MulSIMD(-1, b), a), # b) AddSIMD(MulSIMD(b, -1), a), # c) AddSIMD(a, MulSIMD(-1, b)), and # d) AddSIMD(a, MulSIMD(b, -1)) # with SubSIMD(a, b) for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # AddSIMD(MulSIMD(-1, b), a) >> SubSIMD(a, b) if func == AddSIMD and args[0].func == MulSIMD and \ lookup_rational(args[0].args[0]) == -1: subtree.expr = SubSIMD(args[1], args[0].args[1]) tree.build(subtree, clear=True) # AddSIMD(MulSIMD(b, -1), a) >> SubSIMD(a, b) elif func == AddSIMD and args[0].func == MulSIMD and \ lookup_rational(args[0].args[1]) == -1: subtree.expr = SubSIMD(args[1], args[0].args[0]) tree.build(subtree, clear=True) # AddSIMD(a, MulSIMD(-1, b)) >> SubSIMD(a, b) elif func == AddSIMD and args[1].func == MulSIMD and \ lookup_rational(args[1].args[0]) == -1: subtree.expr = SubSIMD(args[0], args[1].args[1]) tree.build(subtree, clear=True) # AddSIMD(a, MulSIMD(b, -1)) >> SubSIMD(a, b) elif func == AddSIMD and args[1].func == MulSIMD and \ lookup_rational(args[1].args[1]) == -1: subtree.expr = SubSIMD(args[0], args[1].args[0]) tree.build(subtree, clear=True) expr = tree.reconstruct() # Step 4: Now that all multiplication and addition functions only take two # arguments, we can now easily define fused-multiply-add functions, # where AddSIMD(a, MulSIMD(b, c)) = b*c + a = FusedMulAddSIMD(b, c, a), # or AddSIMD(MulSIMD(b, c), a) = b*c + a = FusedMulAddSIMD(b, c, a). # Fused multiply add (FMA3) is standard on Intel CPUs with the AVX2 # instruction set, starting with Haswell processors in 2013: # https://en.wikipedia.org/wiki/Haswell_(microarchitecture) # Step 4.a: Find double FMA patterns first [e.g., FMA(a,b,FMA(c,d,e))]: # NOTE: Double FMA simplifications do not guarantee a significant performance impact when solving BSSN equations: if SIMD_find_more_FMAsFMSs == "True": for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # a + b*c + d*e -> FMA(b,c,FMA(d,e,a)) # AddSIMD(a, AddSIMD(MulSIMD(b,c), MulSIMD(d,e))) >> FusedMulAddSIMD(b, c, FusedMulAddSIMD(d,e,a)) # Validate: # x = a + b*c + d*e # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") if (func == AddSIMD and args[1].func == AddSIMD and args[1].args[0].func == MulSIMD and args[1].args[1].func == MulSIMD): subtree.expr = FusedMulAddSIMD( args[1].args[0].args[0], args[1].args[0].args[1], FusedMulAddSIMD(args[1].args[1].args[0], args[1].args[1].args[1], args[0])) tree.build(subtree, clear=True) # b*c + d*e + a -> FMA(b,c,FMA(d,e,a)) # Validate: # x = b*c + d*e + a # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") # AddSIMD(AddSIMD(MulSIMD(b,c), MulSIMD(d,e)),a) >> FusedMulAddSIMD(b, c, FusedMulAddSIMD(d,e,a)) elif func == AddSIMD and args[0].func == AddSIMD and args[0].args[ 0].func == MulSIMD and args[0].args[1].func == MulSIMD: subtree.expr = FusedMulAddSIMD( args[0].args[0].args[0], args[0].args[0].args[1], FusedMulAddSIMD(args[0].args[1].args[0], args[0].args[1].args[1], args[1])) tree.build(subtree, clear=True) expr = tree.reconstruct() # Step 4.b: Next find single FMA patterns: for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # AddSIMD(MulSIMD(b, c), a) >> FusedMulAddSIMD(b, c, a) if func == AddSIMD and args[0].func == MulSIMD: subtree.expr = FusedMulAddSIMD(args[0].args[0], args[0].args[1], args[1]) tree.build(subtree, clear=True) # AddSIMD(a, MulSIMD(b, c)) >> FusedMulAddSIMD(b, c, a) elif func == AddSIMD and args[1].func == MulSIMD: subtree.expr = FusedMulAddSIMD(args[1].args[0], args[1].args[1], args[0]) tree.build(subtree, clear=True) # SubSIMD(MulSIMD(b, c), a) >> FusedMulSubSIMD(b, c, a) elif func == SubSIMD and args[0].func == MulSIMD: subtree.expr = FusedMulSubSIMD(args[0].args[0], args[0].args[1], args[1]) tree.build(subtree, clear=True) expr = tree.reconstruct() # Step 4.c: Leftover double FMA patterns that are difficult to find in Step 5.a: # NOTE: Double FMA simplifications do not guarantee a significant performance impact when solving BSSN equations: if SIMD_find_more_FMAsFMSs == "True": for subtree in tree.preorder(): func = subtree.expr.func args = subtree.expr.args # (b*c - d*e) + a -> AddSIMD(a, FusedMulSubSIMD(b, c, MulSIMD(d, e))) >> FusedMulSubSIMD(b, c, FusedMulSubSIMD(d,e,a)) # Validate: # x = (b*c - d*e) + a # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") if func == AddSIMD and args[1].func == FusedMulSubSIMD and args[ 1].args[2].func == MulSIMD: subtree.expr = FusedMulSubSIMD( args[1].args[0], args[1].args[1], FusedMulSubSIMD(args[1].args[2].args[0], args[1].args[2].args[1], args[0])) tree.build(subtree, clear=True) # b*c - (a - d*e) -> SubSIMD(FusedMulAddSIMD(b, c, MulSIMD(d, e)), a) >> FMA(b,c,FMS(d,e,a)) # Validate: # x = b * c - (a - d * e) # outputC(x, "x", params="SIMD_enable=True,SIMD_debug=True") elif func == SubSIMD and args[0].func == FusedMulAddSIMD and args[ 0].args[2].func == MulSIMD: subtree.expr = FusedMulAddSIMD( args[0].args[0], args[0].args[1], FusedMulSubSIMD(args[0].args[2].args[0], args[0].args[2].args[1], args[1])) tree.build(subtree, clear=True) # (b*c - d*e) - a -> SubSIMD(FusedMulSubSIMD(b, c, MulSIMD(d, e)), a) >> FMS(b,c,FMA(d,e,a)) # Validate: # x = (b*c - d*e) - a # outputC(x,"x", params="SIMD_enable=True,SIMD_debug=True") elif func == SubSIMD and args[0].func == FusedMulSubSIMD and args[ 0].args[2].func == MulSIMD: subtree.expr = FusedMulSubSIMD( args[0].args[0], args[0].args[1], FusedMulAddSIMD(args[0].args[2].args[0], args[0].args[2].args[1], args[1])) tree.build(subtree, clear=True) expr = tree.reconstruct() if debug == "True": expr_check = eval(str(expr).replace("SIMD", "SIMD_check")) expr_check = expr_check.subs(-1, Symbol('_NegativeOne_')) expr_diff = expr_check - expr_orig # The eval(str(srepr())) below normalizes the expression, # fixing a cancellation issue in SymPy ~0.7.4. expr_diff = eval(str(srepr(expr_diff))) tree_diff = ExprTree(expr_diff) for subtree in tree_diff.preorder(): subexpr = subtree.expr if subexpr.func == Float: if abs(subexpr - Integer(subexpr)) < 1.0e-14: subtree.expr = Integer(item) expr_diff = tree_diff.reconstruct() if expr_diff != 0: simp_expr_diff = simplify(expr_diff) if simp_expr_diff != 0: raise Warning('Expression Difference: ' + str(simp_expr_diff)) return (expr)
def view_tree(expr): """Views a sympy expression tree.""" print(srepr(expr))