def get_minimizing_function_z(self,): r''' Minimizing this function gives the adjusted roots. Gives a function to minimize, its arguments are :math:`x,y,Ts`. Also gives its derivative. Returns: Minimizing function (function): A function of :math:`x,y,*Ts` to minimize in the two variables, :math:`x,y`. ''' expression = self.get_symbolic_frequency_perturbation_z()[0] x,y = sp.symbols('x y', real = True) D = {sp.exp(self.z*T): sp.exp(x*T)*(1j*sp.sin(y*T)+sp.cos(y*T)) for T in self.Ts} expression2 = expression.subs(D) num_real,num_imag = expression2.expand().as_real_imag() diff_x_real = num_real.diff(x) diff_y_imag = num_real.diff(y) func = ufuncify( [x,y]+self.Ts, num_real**2 + num_imag**2) dfunc_x = ufuncify( [x,y]+self.Ts,num_real*diff_x_real) dfunc_y = ufuncify( [x,y]+self.Ts, num_imag*diff_y_imag) return func, lambda x,y,*Ts: np.asarray([dfunc_x(x,y,*Ts),dfunc_y(x,y,*Ts)])
def __init__(self, function_string): symbolic_activation = sympify(function_string) symbolic_activation_d = diff(symbolic_activation) free_variable = list(symbolic_activation.free_symbols)[0] self.act = ufuncify(free_variable, symbolic_activation) self.act_d = ufuncify(free_variable, symbolic_activation_d)
def runtest_ufuncify(language, backend): has_module('numpy') a, b, c = symbols('a b c') fabc = ufuncify([a, b, c], a*b + c, language=language, backend=backend) facb = ufuncify([a, c, b], a*b + c, language=language, backend=backend) grid = numpy.linspace(-2, 2, 50) for b in numpy.linspace(-5, 4, 3): for c in numpy.linspace(-1, 1, 3): expected = grid*b + c assert numpy.sum(numpy.abs(expected - fabc(grid, b, c))) < 1e-13 assert numpy.sum(numpy.abs(expected - facb(grid, c, b))) < 1e-13
def runtest_ufuncify(language, backend): has_module('numpy') a, b, c = symbols('a b c') fabc = ufuncify([a, b, c], a*b + c, backend=backend) facb = ufuncify([a, c, b], a*b + c, backend=backend) grid = numpy.linspace(-2, 2, 50) b = numpy.linspace(-5, 4, 50) c = numpy.linspace(-1, 1, 50) expected = grid*b + c numpy.testing.assert_allclose(fabc(grid, b, c), expected) numpy.testing.assert_allclose(facb(grid, c, b), expected)
def get_ufunc(expr, variable_map): """Numerically optimize expression""" expr = sympify(expr, locals=variable_map) func = ufuncify(expr.free_symbols, expr) formula = 'f{} = {}'.format(tuple(expr.free_symbols), expr) return func, formula
def ufuncify_angular_function(symbolic_function_matrix): """Convert a matrix of symbolical functions into numerical functions Parameters ---------- symbolic_function_matrix : array-like matrix of symbolical functions Returns ------- numerical_function_matrix : numpy.ndarray matrix of numerical functions (numpy ufunc-like functions) """ if len(symbolic_function_matrix.free_symbols) == 1: my_symbol = symbolic_function_matrix.free_symbols.pop() else: raise ValueError("Number of free symbols must be one") num_rows, num_cols = symbolic_function_matrix.shape numerical_function_matrix = np.empty((num_rows, num_cols), dtype=object) for i in range(num_rows): for j in range(num_cols): numerical_function_matrix[i, j] = ufuncify( [my_symbol], symbolic_function_matrix[i, j]) return numerical_function_matrix
def _add_diff_to_cache(self, diff): ''' Symbolically differentiates the RBF and then converts the expression to a function which can be evaluated numerically. ''' logger.debug('Creating a numerical function for the RBF %s with ' 'the derivative %s ...' % (self,str(diff))) dim = len(diff) c_sym = sympy.symbols('c:%s' % dim) x_sym = sympy.symbols('x:%s' % dim) r_sym = sympy.sqrt(sum((xi-ci)**2 for xi, ci in zip(x_sym, c_sym))) # substitute 'r' in the RBF expression with the cartesian spatial # variables and differentiate the RBF with respect to them expr = self.expr.subs(_R, r_sym) for xi, order in zip(x_sym, diff): if order == 0: continue expr = expr.diff(*(xi,)*order) # if `tol` is given, form a separate expression for the RBF near # its center if self.tol is not None: if diff in self.limits: # use a user-specified limit if available lim = self.limits[diff] else: logger.debug('Approximating the value at the RBF center ...') # evaluate the RBF at the point (x0=tol+c0, x1=c1, x2=c2, ...) subs_list = [(x_sym[0], self.tol + c_sym[0])] subs_list += zip(x_sym[1:], c_sym[1:]) # evaluate the RBF and its derivative w.r.t. x0 at that point a = expr.subs(subs_list) b = expr.diff(x_sym[0]).subs(subs_list) # form a linear polynomial and evaluate it at x=c lim = a - self.tol*b # try to simplify the expression to reduce numerical rounding # error. Note that this should only be a function of `eps` now # and the simplification should not take long lim = sympy.cancel(lim) # return any remaining numbers to regular precision floats mapping = {n : float(n) for n in lim.atoms(sympy.Number)} lim = sympy.sympify(lim.xreplace(mapping)) logger.debug('Approximate value at the RBF center: %s' % lim) # create a piecewise symbolic function which is `lim` when # `r_sym < tol` and `expr` otherwise expr = sympy.Piecewise((lim, r_sym < self.tol), (expr, True)) if _SYMBOLIC_TO_NUMERIC_METHOD == 'ufuncify': func = ufuncify(x_sym + c_sym + (_EPS,), expr, backend='numpy') elif _SYMBOLIC_TO_NUMERIC_METHOD == 'lambdify': func = lambdify(x_sym + c_sym + (_EPS,), expr, modules=['numpy']) else: raise ValueError() self._cache[diff] = func logger.debug('The numeric function has been created and cached')
def __init__(self, expr): symbols = list(expr.atoms(sp.Symbol)) symbols.sort(key=lambda s: s.name) self.func = ufuncify(symbols, expr) self.kw = symbols self.expr = expr self.kwstr = map(lambda x: x.name, symbols) self.__doc__ = "f = f(%s)"%(", ".join(self.kwstr))
def _add_diff_to_cache(self, diff): ''' Symbolically differentiates the RBF and then converts the expression to a function which can be evaluated numerically. ''' logger.debug( 'Creating a numerical function for the RBF %s with the derivative %s ...' % (self, str(diff))) dim = len(diff) c_sym = sympy.symbols('c:%s' % dim) x_sym = sympy.symbols('x:%s' % dim) r_sym = sympy.sqrt(sum((xi - ci)**2 for xi, ci in zip(x_sym, c_sym))) # substitute 'r' in the RBF expression with the cartesian spatial variables # and differentiate the RBF with respect to them expr = self.expr.subs(_R, r_sym) for xi, order in zip(x_sym, diff): if order == 0: continue expr = expr.diff(*(xi, ) * order) # if `tol` is given, form a separate expression for the RBF near its center if self.tol is not None: if diff in self.limits: # use a user-specified limit if available lim = self.limits[diff] else: logger.debug( 'Symbolically evaluating the RBF at its center ...') # evaluate the limit of the RBF at (x0=tol+c0, x1=c1, x2=c2, ...) as # tol goes to zero. lim = expr.subs(zip(x_sym[1:], c_sym[1:])) lim = lim.simplify() lim = lim.limit(x_sym[0], c_sym[0]) logger.debug('Value of the RBF at its center: %s' % lim) # create a piecewise symbolic function which is `lim` when `r_sym < tol` # and `expr` otherwise expr = sympy.Piecewise((lim, r_sym < self.tol), (expr, True)) if _SYMBOLIC_TO_NUMERIC_METHOD == 'ufuncify': func = ufuncify(x_sym + c_sym + (_EPS, ), expr, backend='numpy', tempdir=_TEMP_DIR) elif _SYMBOLIC_TO_NUMERIC_METHOD == 'lambdify': func = lambdify(x_sym + c_sym + (_EPS, ), expr, modules=['numpy']) else: raise ValueError() self._cache[diff] = func logger.debug('The numeric function has been created and cached')
def runtest_ufuncify(language, backend): has_numpy() a, b, c = symbols('a b c') f = ufuncify([a, b, c], a*b + c, language=language, backend=backend) grid = numpy.linspace(-2, 2, 50) for b in numpy.linspace(-5, 4, 3): for c in numpy.linspace(-1, 1, 3): expected = grid*b + c assert numpy.sum(numpy.abs(expected - f(grid, b, c))) < 1e-13
def runtest_ufuncify(language, backend): has_module('numpy') a, b, c = symbols('a b c') f = ufuncify([a, b, c], a * b + c, language=language, backend=backend) grid = numpy.linspace(-2, 2, 50) for b in numpy.linspace(-5, 4, 3): for c in numpy.linspace(-1, 1, 3): expected = grid * b + c assert numpy.sum(numpy.abs(expected - f(grid, b, c))) < 1e-13
def set_equations(self): x,y,t = symbols('x y t') self.Ps_symbols={} for key in self.p.keys(): self.Ps_symbols[key] = symbols(key) p=self.Ps_symbols if self.setup['rhs']=="RM_forced": """ Normal mode """ from sympy.functions import sin as symsin forcing=(1.0+p['a']*symsin(2.0*np.pi*p['omegaf']*t)) predation_exp=((p['c']*x*y)/(p['b']+x)) self.dxdt_eq = p['r']*x*(1.0-x/p['K'])-predation_exp self.dydt_eq = forcing*p['e']*predation_exp-p['d']*y elif self.setup['rhs']=="RM": """ Normal mode """ from sympy.functions import sin as symsin forcing=(1.0+p['a']*symsin(2.0*np.pi*p['omegaf']*t)) self.dxdt_eq = p['r']*x*(1.0-x/p['K'])-((p['c']*x*y)/(p['b']+x)) self.dydt_eq = p['e']*((p['c']*x*y)/(p['b']+x))-p['d']*y """ Creating numpy functions """ from sympy import solve,Eq self.xs_sym=p['b']*p['d']/(p['e']*p['c']-p['d']) self.ys_sym=solve(Eq(self.dxdt_eq/x,0),y)[0].subs(x,self.xs_sym) self.xs = ufuncify([p['r'],p['K'],p['e'],p['a'],p['omegaf']],[self.sub_parms(self.xs_sym)]) self.ys = ufuncify([p['r'],p['K'],p['e'],p['a'],p['omegaf']],[self.sub_parms(self.ys_sym)]) symeqs = Matrix([self.dxdt_eq,self.dydt_eq]) self.ode = lambdify((x,y,t,p['r'],p['K'],p['e'],p['a'],p['omegaf']),self.sub_parms(symeqs),"numpy",dummify=False) self.dxdt = ufuncify([x,y,t,p['r'],p['K'],p['e'],p['a'],p['omegaf']],[self.sub_parms(self.dxdt_eq)]) self.dydt = ufuncify([x,y,t,p['r'],p['K'],p['e'],p['a'],p['omegaf']],[self.sub_parms(self.dydt_eq)]) self.predation_eq = ufuncify([x,y,t,p['r'],p['K'],p['e'],p['a'],p['omegaf']],[self.sub_parms(predation_exp)]) localJac = symeqs.jacobian(Matrix([x,y])) self.sym_localJac = localJac self.localJac = lambdify((x,y,t,p['r'],p['K'],p['e'],p['a'],p['omegaf']),self.sub_parms(localJac),"numpy",dummify=False) if self.setup['setPDE'] and self.setup['analyze']: self.dbdb = ufuncify([x,y,p['chi'],p['beta']],[self.sub_parms(localJac[0,0])]) self.dbds1 = ufuncify([x,y,p['chi'],p['beta']],[self.sub_parms(localJac[0,1])]) self.dbds2 = ufuncify([x,y,p['chi'],p['beta']],[self.sub_parms(localJac[0,2])]) self.ds1db = ufuncify([x,y,p['chi'],p['beta']],[self.sub_parms(localJac[1,0])]) k = symbols('k') delta_y = symbols('delta_y') symeqs_lin_analysis = Matrix([self.dxdt_eq-x*k*k,self.dydt_eq-y*delta_y*k*k]) jaclinanalysis = symeqs_lin_analysis.jacobian(Matrix([x,y])) self.symbolic_jaclinanalysis = jaclinanalysis self.jaclinanalysis = lambdify((x,y,k),self.sub_parms(jaclinanalysis),"numpy",dummify=False) if self.verbose: self.print_equations() print "Local Jacobian:" ,localJac if self.setup['setPDE'] and self.setup['analyze']: print "Linear analysis Jacobian: ", jaclinanalysis
def __init__(self, t_bound, length1, length2, mass1, mass2, gravity=10, q1_0=0, q2_0=0, dq1dt_0=0, dq2dt_0=0, max_step=1): x1, x2, y1, y2, m1, m2, l1, l2, t1, q1, t2, q2, eom, H, p1, p2, g, B = _create_symbols() # reduce with symmetric length and mass and set the parameters # coordinates self.coords_expr = sym.Matrix([x1, y1, x2, y2]).subs( {m1: mass1, m2: mass2, l1: length1, l2: length2, t1: q1, t2: q2}) self.coords_num = [ufuncify([q1, q2], e) for e in self.coords_expr] # self.H_expr = H.subs({m1:mass1,m2:mass2,l1:length1,l2:length2,g:gravity}).simplify() # self.B_expr = B.subs({m1:mass1,m2:mass2,l1:length1,l2:length2}) self.eom = eom.subs({m1: mass1, m2: mass2, l1: length1, l2: length2, g: gravity}) self.H = H.subs({m1: mass1, m2: mass2, l1: length1, l2: length2, g: gravity}) self.eom_num = [ufuncify([q1, q2, p1, p2], e) for e in self.eom] self.H_num = ufuncify([q1, q2, p1, p2], self.H) # calculate initial generalized momentum self.PQ = B.subs({m1: mass1, m2: mass2, l1: length1, l2: length2, g: gravity}) self.invPQ = self.PQ.inv() * sym.Matrix([[p1], [p2]]) self.invPQ_num = [ufuncify([q1, q2, p1, p2], pq) for pq in self.invPQ] p0 = self.PQ * sym.Matrix([[dq1dt_0], [dq2dt_0]]) sp.integrate.RK45.__init__(self, self.grad, 0, [q1_0, q2_0, p0[0], p0[1]], max_step=max_step, t_bound=t_bound, vectorized=True) self.y_old_old = None
def create_pde_eqs(self): from sympy.utilities.autowrap import ufuncify from utilities.laplacian_sparse import create_laplacian self.lapmat = create_laplacian(self.m.setup['n'], self.m.setup['l'], self.m.setup['bc'], self.m.p['diffusion']) self.reac = [] for i, var in enumerate(self.sym_uh[:-1]): self.reac.append( ufuncify(self.sym_uh, [self.sub_parms(self.symeqs[i])]))
def _get_newtons_func(self,expression): ''' Takes an expression in terms of Ts and sp.exp(-z*T) for T in Ts, and returns a complex-valued function for it. Here :math:`z = x + i y` is a complex number. Args: expression (sympy expression): A symbolic expression. Returns: a function in x,y,Ts that returns the value of the input expression. ''' x,y = sp.symbols('x y', real = True) D = {sp.exp(self.z*T): sp.exp(x*T)*(1j*sp.sin(y*T)+sp.cos(y*T)) for T in self.Ts} expression2 = expression.subs(D) num_real,num_imag = expression2.expand().as_real_imag() f_r = ufuncify( [x,y]+self.Ts, num_real) f_i = ufuncify( [x,y]+self.Ts, num_imag) return lambda x,y,Ts: f_r(x,y,*Ts)+f_i(x,y,*Ts)*1j
def animacion(N): AA = [] BB = [] CC = [] x = np.linspace(-np.pi / 2, 3 * np.pi / 2, 50) y = np.linspace(0, 1, 50) X, Y = np.meshgrid(x, y) jones1 = Matrix([0, 1]) jones0 = Matrix([1, 0]) M1 = Matrix([[1, 0], [0, 1]]) A = Matrix([[1, 0], [0, beta * (cos(theta) + I * sin(theta))]]) r = jones0 for i in range(2, N + 1): K = N_mach(N, 2) if i == N - 1: r = simplify(K * r) else: r = simplify(M1 * A * K * r) pd1 = jones0.T * r pd1 = pd1[0] * conjugate(pd1[0]) pd1 = re(pd1) pd2 = jones1.T * r pd2 = pd2[0] * conjugate(pd2[0]) pd2 = re(pd2) pabs = 1 - pd1 - pd2 pd1 = simplify(pd1) f = ufuncify((theta, beta), pd1) Z = f(X, Y) pd2 = simplify(pd2) f2 = ufuncify((theta, beta), pd2) Z2 = f2(X, Y) pabs = simplify(pabs) f3 = ufuncify((theta, beta), pabs) Z3 = f3(X, Y) AA.append(Z) BB.append(Z2) CC.append(Z3) print(i) np.save('nuevoPD1hastan' + str(i) + '.npy', AA) np.save('nuevoPD2hastan' + str(i) + '.npy', BB) np.save('nuevoPabshastan' + str(i) + '.npy', CC) return AA, BB, CC
def SymToUfuncify(expression,**args): # expression = sym.simplify(expression) required_vars = varExprNames(expression) if len(args)!=0: def_varNames, def_values = [], [] # Structure the default values: for var in args: if var in required_vars: def_varNames.append(var) def_values.append(args[var]) def_symVar = [sym.symbols(var_name) for var_name in def_varNames] # Find the missing ones and order them if an order is passed: missing_varNames = [] for var in required_vars: if var not in def_varNames: missing_varNames.append(var) if "order_vars" in args: # Check not to have too many variables and if so delete them: for i, var in enumerate(args["order_vars"]): if var not in missing_varNames: args["order_vars"].pop(i) # Check to have all of them: if len(args["order_vars"])!=len(missing_varNames): print "Not all vars passed! Required:" print missing_varNames return -1 missing_varNames = args["order_vars"] missing_symVar = [sym.symbols(var_name) for var_name in missing_varNames] # Compile the ufuncify function: if "NIntegrate" in args: expression.subs( [ (symVar,val) for symVar, val in zip(def_symVar,def_values) ] ) # Output a one-only input function: return ufuncify(missing_symVar, expression) else: return [ufuncify(missing_symVar+def_symVar, expression), missing_varNames, def_varNames, def_values] else: # Just use the default sympy ordering: return [ufuncify(varExpr(expression), expression), required_vars]
def ufunc_expr(symbols, formula): """Ufuncify the formula with symbols as arguments using numpy backend. Example: foo = ufunc_expr('x y', 'x % y') """ if isinstance(symbols, str): symbols = sy.symbols(symbols) if isinstance(formula, str): formula = sympify(formula) return ufuncify(symbols, formula, backend='numpy', flags=['-D_USE_MATH_DEFINES'])
def get_feature_combinations(feature_tuples): # new features as combinations of two other features func_combinations = { "x+y": lambda x, y: x + y, "x*y": lambda x, y: x * y, "x-y": lambda x, y: x - y, "y-x": lambda x, y: y - x } # get all feature combinations for the given feature tuples # modifies global variables df and feature_pool! nonlocal df, feature_pool, units # returns a list of new features that were generated new_features = [] # store all new features in a preallocated numpy array before adding it to the dataframe feat_array = np.zeros( (df.shape[0], len(feature_tuples) * len(func_combinations)), dtype=np.float32) for i, (feat1, feat2) in enumerate(feature_tuples): if not i % 100: print("%15i/%15i" % (i, len(feature_tuples)), end="\r") for fc in func_combinations: expr = func_combinations[fc](feature_pool[feat1], feature_pool[feat2]) expr_name = str(expr) if expr_name not in feature_pool: # if we're given units, check if the operation is legal if units: try: units[expr_name] = func_combinations[fc]( units[feat1], units[feat2]) units[expr_name].__dict__["_magnitude"] = 1. except (pint.DimensionalityError, pint.OffsetUnitCalculusError) as e: continue feature_pool[expr_name] = expr # create temporary variable expression to apply it to precomputed features s, t = sympy.symbols("s t") expr_temp = func_combinations[fc](s, t) f = ufuncify((s, t), expr_temp) new_feat = np.array(f(df[feat1].values, df[feat2].values), dtype=np.float32) if np.isfinite(new_feat).all(): feat_array[:, len(new_features)] = new_feat new_features.append(expr_name) print("%15i/%15i done." % (len(new_features), len(feature_tuples))) df = df.join( pd.DataFrame(feat_array[:, :len(new_features)], columns=new_features, index=df.index, dtype=np.float32)) return new_features
def synthesis(self) -> None: """ Метод синтеза закона управления. Закон управления минтезируется путем составления последовательных прогнозов на некоторое количество тактов вперед, пока в выражении не появиться u(t). После чего этот прогноз приравнивается к желаемой траектории на данном такте и из получившегося равенства выражается управление(u(t)). Если объект описывается уравнением: x(t) = f(x(t-1), u(t-1-tao), a) + e(t), t=1,2,3,... то модель будет выглядеть следующим образом: y(t|a(t)) = f(x(t-1), u(t-1-tao), a(t)) Прогноз на 1 такт вперед: y(t+1|a(t)) = f(x(t), u(t-tao), a(t)) повторение процедуры пока не появиться u(t): y(t+1+tao|a(t)) = f(y(t+tao|a(t)), u(t), a(t)) Приравнивание у желаемой трактории: y(t+1+tao|a(t)) = x(t+1+tao)* Выражение закона управления сохраняется в self._regulator_expr, а функция в self._regulator_func. :return: None """ min_tao: int = min([g.min_tao for g in self._predicted_vars['input']]) step = 0 while step < min_tao: self.forecast_one_step() self.expr_subs() if step == 0: self._base_vars = {k: copy.deepcopy(v) for k, v in self._predicted_vars.items()} step += 1 sp_var = [] sp_current_vars = dict() for k, v in self._predicted_vars.items(): sp_current_vars[k] = [] for item in support.flatten([g.variables for g in v]): if item.tao != 0: sp_var.append(sp.var(item.name)) else: sp_current_vars[k].append(sp.var(item.name)) sp_vars_a = sp.var([i.name for i in self._model.coefficients]) self._args = [*sp_var, *sp_vars_a, self._desired_output_sp, *np.hstack([v for k, v in sp_current_vars.items() if k != 'input'])] expr = sp.solve(self._expr - self._desired_output_sp, sp_current_vars['input'][0]) self._regulator_expr = expr[0] self._regulator_func = ufuncify(self._args, expr[0])
def _get_newtons_func(self, expression): ''' Takes an expression in terms of Ts and sp.exp(-z*T) for T in Ts, and returns a complex-valued function for it. Here :math:`z = x + i y` is a complex number. Args: expression (sympy expression): A symbolic expression. Returns: a function in x,y,Ts that returns the value of the input expression. ''' x, y = sp.symbols('x y', real=True) D = { sp.exp(self.z * T): sp.exp(x * T) * (1j * sp.sin(y * T) + sp.cos(y * T)) for T in self.Ts } expression2 = expression.subs(D) num_real, num_imag = expression2.expand().as_real_imag() f_r = ufuncify([x, y] + self.Ts, num_real) f_i = ufuncify([x, y] + self.Ts, num_imag) return lambda x, y, Ts: f_r(x, y, *Ts) + f_i(x, y, *Ts) * 1j
def torus_dat(kp, kq, refine=300, segm=40, tR=1.6, tr=0.6): spt, spp, spq, spr, spR = sp.symbols("t p q r R", real=True) c = sp.Matrix([(spR+spr*sp.cos(2*sp.pi*spq*spt))*sp.cos(2*sp.pi*spp*spt),\ (spR+spr*sp.cos(2*sp.pi*spq*spt))*sp.sin(2*sp.pi*spp*spt),\ spr*sp.sin(2*sp.pi*spq*spt)]) dc = sp.Matrix([sp.diff(x,spt) for x in c]) # derivative ldc = sp.sqrt(sum( [ x**2 for x in dc ] )).simplify() # speed udc = dc/ldc ## 2nd order kc = sp.Matrix([sp.diff(x,spt) for x in udc]) # curvature vector ks = sp.sqrt(sum( [ x**2 for x in kc])) # curvature scalar ukc = kc/ks # unit curvature vector ## bi-normal bnc = udc.cross(ukc) # cross of unit tangent and unit curvature. ## the parametrization of the boundary of the width w tubular neighbourhood spw, spu = sp.symbols("w, u", real=True) ## width of torus knot, and meridional parameter tSurf = c + spw*sp.cos(2*sp.pi*(spu+kp*kq*spt))*ukc + spw*sp.sin(2*sp.pi*(spu+kp*kq*spt))*bnc ## (b) ufuncify from sympy.utilities.autowrap import ufuncify knotSuf = [ufuncify([spt, spp, spq, spr, spR, spw, spu], tSurf[i]) for i in range(3)] knotSnp = sp.lambdify((spt, spp, spq, spr, spR, spw, spu), tSurf, "numpy" ) kt = (np.pi*tr) / (4*kp) # knot radial thickness 2*pi*tr is circumf, and kp strands pass through so this ## should be around 2*pi*tr would be 2*kp*kt for the knot to fill the surface, i.e kt = pi*tr / 4*kp ## make bigger or smaller depending on how much empty space one wants to see. seg = kp*refine ## segments along length of pq torus knot. kp*120 gives a fairly smooth image. def surf(i,j): ## lambdify return np.array(knotSnp(float(i)/seg, kp, kq, tr, tR, kt, float(j)/segm)).ravel() fp = FloatProgress(min=0, max=100, description="Knot data"); display(fp); ## progrss indicator xyz = np.ndarray( (seg+1, segm+1, 3) ) for i,j in it.product( range(seg+1), range(segm+1) ): ## put the affine reparametrization here. xyz[i,j] = surf(i,j) fp.value = int(100*i/(seg+1)) fp.close() return(xyz)
def _generate_features(self, df, new_feat_cols): """ Generate additional features based on the feature formulas for all data points in the df. Only works after the model was fitted. Inputs: - df: pandas dataframe with original features - new_feat_cols: names of new features that should be generated (keys of self.feature_formulas) Returns: - df: dataframe with the additional feature columns added """ assert new_feat_cols[0] in self.feature_formulas,\ "[AutoFeatRegression] First call fit or fit_transform to generate the features!" print("[AutoFeatRegression] Computing %i new features." % len(new_feat_cols)) # generate all good feature; unscaled this time feat_array = np.zeros((len(df), len(new_feat_cols))) for i, expr in enumerate(new_feat_cols): print("[AutoFeatRegression] %5i/%5i" % (i, len(new_feat_cols)), end="\r") if expr not in self.feature_functions: # generate a substitution expression based on all the original symbols of the original features # for the given generated feature in good cols # since sympy can handle only up to 32 original features in ufunctify, we need to check which features # to consider here, therefore perform some crude check to limit the number of features used cols = [c for c in self.feateng_cols if c in expr] try: f = ufuncify((self.feature_formulas[c] for c in cols), self.feature_formulas[expr]) except: print( "[AutoFeatRegression] Error while processing expression: %r" % expr) raise self.feature_functions[expr] = (cols, f) else: cols, f = self.feature_functions[expr] try: feat_array[:, i] = f(*(df[c].values for c in cols)) except RuntimeWarning as e: print( "[AutoFeatRegression] Problem while evaluating expression: %r with columns %r - are maybe some values 0 that shouldn't be?" % (expr, cols)) raise print("[AutoFeatRegression] %5i/%5i ...done." % (len(new_feat_cols), len(new_feat_cols))) df = df.join( pd.DataFrame(feat_array, columns=new_feat_cols, index=df.index)) return df
def __init__(self, var_names_and_syms={}, dict_or_expr={}): if hasattr(dict_or_expr, 'keys'): for k, v in dict_or_expr.items(): dict_or_expr[k] = CompyledFunc(var_names_and_syms=var_names_and_syms, dict_or_expr=v) self.Compyled = dict_or_expr elif is_non_atomic_sympy_expr(dict_or_expr): self.Vars = tuple(var for var, symbol in var_names_and_syms.items() if symbol and not(isinstance(symbol, FLOAT_TYPES))) inputs = (var_names_and_syms[var] for var in self.Vars) if use_theano: self.Compyled = theano_function(inputs, (dict_or_expr,), allow_input_downcast=True) else: self.Compyled = ufuncify(inputs, dict_or_expr) else: self.Compyled = sympy_to_float(dict_or_expr)
def ufunc_expr(symbols, formula, consts=PROBLEM_CONSTS): """Ufuncify the formula with symbols as arguments using numpy backend. Example: foo = ufunc_expr('x y', 'x % y') """ if isinstance(symbols, str): symbols = sy.symbols(symbols) if isinstance(formula, str): formula = sympify(formula) if consts is not None: formula = formula.subs( [(sy.Symbol(k), v) for k, v in consts.items()] ) return ufuncify(symbols, formula, backend='numpy', flags=['-D_USE_MATH_DEFINES'])
def main(): print(__doc__) x = symbols('x') # a numpy array we can apply the ufuncs to grid = np.linspace(-1, 1, 1000) # set mpmath precision to 20 significant numbers for verification mpmath.mp.dps = 20 print("Compiling legendre ufuncs and checking results:") # Let's also plot the ufunc's we generate plot1 = Plot(visible=False) for n in range(6): # Setup the SymPy expression to ufuncify expr = legendre(n, x) print("The polynomial of degree %i is" % n) pprint(expr) # This is where the magic happens: binary_poly = ufuncify(x, expr) # It's now ready for use with numpy arrays polyvector = binary_poly(grid) # let's check the values against mpmath's legendre function maxdiff = 0 for j in range(len(grid)): precise_val = mpmath.legendre(n, grid[j]) diff = abs(polyvector[j] - precise_val) if diff > maxdiff: maxdiff = diff print("The largest error in applied ufunc was %e" % maxdiff) assert maxdiff < 1e-14 # We can also attach the autowrapped legendre polynomial to a sympy # function and plot values as they are calculated by the binary function g = implemented_function('g', binary_poly) plot1[n] = g(x), [200] print( "Here's a plot with values calculated by the wrapped binary functions") plot1.show()
def main(): print(__doc__) x = symbols('x') # a numpy array we can apply the ufuncs to grid = np.linspace(-1, 1, 1000) # set mpmath precision to 20 significant numbers for verification mpmath.mp.dps = 20 print("Compiling legendre ufuncs and checking results:") # Let's also plot the ufunc's we generate for n in range(6): # Setup the SymPy expression to ufuncify expr = legendre(n, x) print("The polynomial of degree %i is" % n) pprint(expr) # This is where the magic happens: binary_poly = ufuncify(x, expr) # It's now ready for use with numpy arrays polyvector = binary_poly(grid) # let's check the values against mpmath's legendre function maxdiff = 0 for j in range(len(grid)): precise_val = mpmath.legendre(n, grid[j]) diff = abs(polyvector[j] - precise_val) if diff > maxdiff: maxdiff = diff print("The largest error in applied ufunc was %e" % maxdiff) assert maxdiff < 1e-14 # We can also attach the autowrapped legendre polynomial to a sympy # function and plot values as they are calculated by the binary function plot1 = plt.pyplot.plot(grid, polyvector, hold=True) print("Here's a plot with values calculated by the wrapped binary functions") # plt.pyplot.show() pltshow(plt)
def __init__(self, var_names_and_syms={}, dict_or_expr={}): if hasattr(dict_or_expr, 'keys'): for k, v in list(dict_or_expr.items()): dict_or_expr[k] = CompyledFunc( var_names_and_syms=var_names_and_syms, dict_or_expr=v) self.Compyled = dict_or_expr elif is_non_atomic_sympy_expr(dict_or_expr): self.Vars = tuple( var for var, symbol in list(var_names_and_syms.items()) if symbol and not (isinstance(symbol, FLOAT_TYPES))) inputs = (var_names_and_syms[var] for var in self.Vars) if use_theano: self.Compyled = theano_function(inputs, (dict_or_expr, ), allow_input_downcast=True) else: self.Compyled = ufuncify(inputs, dict_or_expr) else: self.Compyled = sympy_to_float(dict_or_expr)
def __init__(self, variables, parameters, expression_dict): """ Class to create a ode function from symbolic expressions :param variables: list of symbols :param parameters: list of sybols :param expression_dict: dict of expressions for the rate of change of each variable indexed by variable symbols {v1: p1*var1*var2, var2: ...} """ self.variables = variables self.parameters = parameters self.input = variables + parameters self.expressions = [expression_dict[v] for v in variables] self.function = [] for e in self.expressions: self.function.append(ufuncify(self.input, e, backend='Cython'))
def __call__(self,x,c,eps=None,diff=None): ''' Evaluates the RBF Parameters ---------- x : (N,D) array evaluation points c : (M,D) array RBF centers eps : (M,) array, optional shape parameters for each RBF. Defaults to 1.0 diff : (D,) int array, optional Tuple indicating the derivative order for each spatial dimension. For example, if there are three spatial dimensions then providing (2,0,1) would return the RBF after differentiating it twice along the first axis and once along the third axis. Returns ------- out : (N,M) array Returns the RBFs with centers *c* evaluated at *x* Notes ----- This function evaluates the RBF and its derivatives symbolically using sympy and then the symbolic expression is converted to a numerical function. The numerical function is cached and then reused when this function is called multiple times with the same derivative specification. ''' x = np.asarray(x,dtype=float) c = np.asarray(c,dtype=float) if eps is None: eps = np.ones(c.shape[0],dtype=float) else: eps = np.asarray(eps,dtype=float) if diff is None: diff = (0,)*x.shape[1] else: # make sure diff is immutable diff = tuple(diff) # make sure the input arguments have the proper dimensions if not ((x.ndim == 2) & (c.ndim == 2)): raise ValueError( 'x and c must be two-dimensional arrays') if not (x.shape[1] == c.shape[1]): raise ValueError( 'x and c must have the same number of spatial dimensions') if not ((eps.ndim == 1) & (eps.shape[0] == c.shape[0])): raise ValueError( 'eps must be a one-dimensional array with length equal to ' 'the number of rows in c') if not (len(diff) == x.shape[1]): raise ValueError( 'diff must have the same length as the number of spatial ' 'dimensions in x and c') # expand to allow for broadcasting x = x[:,None,:] c = c[None,:,:] # this does the same thing as np.rollaxis(x,-1) but is much faster x = np.einsum('ijk->kij',x) c = np.einsum('ijk->kij',c) # add function to cache if not already if diff not in self.cache: dim = len(diff) c_sym = sympy.symbols('c:%s' % dim) x_sym = sympy.symbols('x:%s' % dim) r_sym = sympy.sqrt(sum((x_sym[i]-c_sym[i])**2 for i in range(dim))) expr = self.expr.subs(_R,r_sym) for direction,order in enumerate(diff): if order == 0: continue expr = expr.diff(*(x_sym[direction],)*order) if _SYM_TO_NUM == 'numpy': func = sympy.lambdify(x_sym+c_sym+(_EPS,),expr,'numpy') func = _check_lambdified_output(func) self.cache[diff] = func elif _SYM_TO_NUM == 'cython': func = ufuncify(x_sym+c_sym+(_EPS,),expr) self.cache[diff] = func args = (tuple(x)+tuple(c)+(eps,)) return self.cache[diff](*args)
yj_sp_p = sp.lambdify([x_sp, eps_sp], ((x_sp + 1)**(1 + eps_sp) - 1) / (1 + eps_sp), 'sympy') yj_sp_n = sp.lambdify([x_sp, eps_sp], -((-x_sp + 1)**(1 - eps_sp) - 1) / (1 - eps_sp), 'sympy') jy_sp_p = sp.lambdify([x_sp, eps_sp], ((1 + eps_sp) * x_sp + 1)**(1 / (1 + eps_sp)) - 1, 'sympy') jy_sp_n = sp.lambdify([x_sp, eps_sp], -(-(1 - eps_sp) * x_sp + 1)**(1 / (1 - eps_sp)) + 1, 'sympy') sa_sp_p = sp.lambdify([x_sp, eta_sp], sp.sinh(eta_sp * x_sp) / eta_sp, 'sympy') sa_sp_o = sp.lambdify([x_sp, eta_sp], x_sp, 'sympy') sa_sp_n = sp.lambdify([x_sp, eta_sp], sp.asinh(eta_sp * x_sp) / eta_sp, 'sympy') _to_gauss = ufuncify(args = [x_sp, eta_sp, eps_sp, beta_sp], expr = sp.Piecewise((sa_sp_o(yj_nested_tool(yj_sp_p, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, sp.And(eta_sp > -_sp_eps, eta_sp < _sp_eps, x_sp >= 0)), (sa_sp_o(yj_nested_tool(yj_sp_n, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, sp.And(eta_sp > -_sp_eps, eta_sp < _sp_eps, x_sp < 0)), (sa_sp_p(yj_nested_tool(yj_sp_p, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, sp.And(eta_sp > 0, x_sp >= 0)), (sa_sp_p(yj_nested_tool(yj_sp_n, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, sp.And(eta_sp > 0, x_sp < 0)), (sa_sp_n(yj_nested_tool(yj_sp_p, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, sp.And(eta_sp < 0, x_sp >= 0)), (sa_sp_n(yj_nested_tool(yj_sp_n, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, sp.And(eta_sp < 0, x_sp < 0)), (sp.nan, True)), backend='cython') # backend='numpy') _to_gauss_g = ufuncify(args = [x_sp, eta_sp, eps_sp, beta_sp], expr = sp.Piecewise((sp.diff(sa_sp_o(yj_nested_tool(yj_sp_p, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, x_sp, 1), sp.And(eta_sp > -_sp_eps, eta_sp < _sp_eps, x_sp >= 0)), (sp.diff(sa_sp_o(yj_nested_tool(yj_sp_n, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, x_sp, 1), sp.And(eta_sp > -_sp_eps, eta_sp < _sp_eps, x_sp < 0)), (sp.diff(sa_sp_p(yj_nested_tool(yj_sp_p, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, x_sp, 1), sp.And(eta_sp > 0, x_sp >= 0)), (sp.diff(sa_sp_p(yj_nested_tool(yj_sp_n, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, x_sp, 1), sp.And(eta_sp > 0, x_sp < 0)), (sp.diff(sa_sp_n(yj_nested_tool(yj_sp_p, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, x_sp, 1), sp.And(eta_sp < 0, x_sp >= 0)), (sp.diff(sa_sp_n(yj_nested_tool(yj_sp_n, _yj_n)(x_sp / beta_sp, eps_sp), eta_sp) * beta_sp, x_sp, 1), sp.And(eta_sp < 0, x_sp < 0)), (sp.nan, True)), backend='cython')
def funcify_all(items, param): return [ufuncify([param],item) for item in items]
def test_ufuncify(): x, y = symbols("x y") f = ufuncify((x, y), x + y, backend="dummy") assert f() == "f(_x[_i], y)"
def test_ufuncify(): x, y = symbols('x y') f = ufuncify((x, y), x + y, backend='dummy') assert f() == "f(_x[_i], y)"
lfun = lambdify((x, y), expr) ts = time.time() for i in range(NN): lfun(2.0, 3.0) te = time.time() print "lambdify: ", (te - ts) ts = time.time() for i in range(NN): py_fun(2.0, 3.0) #ee.run_function(fun, [arg1, arg2]) te = time.time() print "py_fun: ", (te - ts) # ts = time.time() # for i in range(NN): # expr.subs({x:2.0, y:3.0}) # te = time.time() # print (te-ts) fn_fortran = ufuncify([x, y], expr) print fn_fortran(2.0, 3.0) ts = time.time() for i in range(NN): fn_fortran(2.0, 3.0) te = time.time() print "fn_fortran: ", (te - ts)
def funcify_all(items, param): return [ufuncify([param], item) for item in items]
def _f(fcn): fcn = fcn.replace("x**2", "x*x") fcn = fcn.replace("y**2", "y*y") fcn = fcn.replace("x", "x[0]") fcn = fcn.replace("y", "x[1]") if "/4" in fcn: fcn = "(%s)*0.25;" % (fcn.replace("/4", "")) if "/8" in fcn: fcn = "(%s)*0.125;" % (fcn.replace("/8", "")) return fcn print("B[%2d] = " % (2 * k) + _f("%s" % (ux))) print("B[%2d] = " % (2 * k + 1) + _f("%s" % (uy))) if plot: X, Y = np.meshgrid(np.linspace(-1, 1, 9), np.linspace(-1, 1, 9)) uxy = ufuncify((x, y), ux) vxy = ufuncify((x, y), uy) ax[j, i].quiver(X, Y, uxy(X, Y), vxy(X, Y)) ax[j, i].set_title("v%d%d = [%s, %s]" % (i + 1, j + 1, ux, uy)) ax[j, i].plot([-1, 1, 1, -1, -1], [-1, -1, 1, 1, -1], '-k') ax[j, i].set_xlim([-1.5, 1.5]) ax[j, i].set_ylim([-1.5, 1.5]) print("b%d%d = [%s, %s]" % (i + 1, j + 1, ux, uy)) if plot: plt.show() if False: print("PetscReal N11(PetscReal x,PetscReal y) return %s; " % (vx.coeff(v11).evalf())) print("PetscReal N12(PetscReal x,PetscReal y) return %s; " % (vy.coeff(v12).evalf())) print("PetscReal N21(PetscReal x,PetscReal y) return %s; " %
# Circle / Oval / Lemniscate p_symb = sp.Matrix([cx + rx*sp.sin(2*omega*(t+theta)), cy + ry*sp.sin(omega*(t+theta)), cz + rz*sp.cos(omega*(t+theta)) ]) # # Straight line - almost constant # p_symb = sp.Matrix([ 0.001*t, # 0.0, # -1.0]) # In[14]: p_vec = stack([ufuncify(t,p_symb[i].subs(args)) for i in arange(n_dims)]) v_vec = stack([ufuncify(t,p_symb.diff(t)[i].subs(args)) for i in arange(n_dims)]) a_vec = stack([ufuncify(t,p_symb.diff(t,2)[i].subs(args)) for i in arange(n_dims)]) da_vec = stack([ufuncify(t,p_symb.diff(t,3)[i].subs(args)) for i in arange(n_dims)]) d2a_vec = stack([ufuncify(t,p_symb.diff(t,4)[i].subs(args)) for i in arange(n_dims)]) def pr(x_orig): x = asarray(x_orig).reshape(-1,1) vec = stack([p_vec[i](x) for i in range(len(p_vec))],axis=1).reshape(-1,1,3,1) # If first dimension is singleton then remove it if(vec.shape[0]==1): vec = vec.reshape(vec.shape[2:]) return vec
def main(): print(__doc__) # arrays are represented with IndexedBase, indices with Idx m = Symbol('m', integer=True) i = Idx('i', m) A = IndexedBase('A') B = IndexedBase('B') x = Symbol('x') print("Compiling ufuncs for radial harmonic oscillator solutions") # setup a basis of ho-solutions (for l=0) basis_ho = {} for n in range(basis_dimension): # Setup the radial ho solution for this n expr = R_nl(n, orbital_momentum_l, omega2, x) # Reduce the number of operations in the expression by eval to float expr = expr.evalf(15) print("The h.o. wave function with l = %i and n = %i is" % ( orbital_momentum_l, n)) pprint(expr) # implement, compile and wrap it as a ufunc basis_ho[n] = ufuncify(x, expr) # now let's see if we can express a hydrogen radial wave in terms of # the ho basis. Here's the solution we will approximate: H_ufunc = ufuncify(x, hydro_nl(hydrogen_n, orbital_momentum_l, 1, x)) # The transformation to a different basis can be written like this, # # psi(r) = sum_i c(i) phi_i(r) # # where psi(r) is the hydrogen solution, phi_i(r) are the H.O. solutions # and c(i) are scalar coefficients. # # So in order to express a hydrogen solution in terms of the H.O. basis, we # need to determine the coefficients c(i). In position space, it means # that we need to evaluate an integral: # # psi(r) = sum_i Integral(R**2*conj(phi(R))*psi(R), (R, 0, oo)) phi_i(r) # # To calculate the integral with autowrap, we notice that it contains an # element-wise sum over all vectors. Using the Indexed class, it is # possible to generate autowrapped functions that perform summations in # the low-level code. (In fact, summations are very easy to create, and as # we will see it is often necessary to take extra steps in order to avoid # them.) # we need one integration ufunc for each wave function in the h.o. basis binary_integrator = {} for n in range(basis_dimension): # # setup basis wave functions # # To get inline expressions in the low level code, we attach the # wave function expressions to a regular SymPy function using the # implemented_function utility. This is an extra step needed to avoid # erroneous summations in the wave function expressions. # # Such function objects carry around the expression they represent, # but the expression is not exposed unless explicit measures are taken. # The benefit is that the routines that searches for repeated indices # in order to make contractions will not search through the wave # function expression. psi_ho = implemented_function('psi_ho', Lambda(x, R_nl(n, orbital_momentum_l, omega2, x))) # We represent the hydrogen function by an array which will be an input # argument to the binary routine. This will let the integrators find # h.o. basis coefficients for any wave function we throw at them. psi = IndexedBase('psi') # # setup expression for the integration # step = Symbol('step') # use symbolic stepsize for flexibility # let i represent an index of the grid array, and let A represent the # grid array. Then we can approximate the integral by a sum over the # following expression (simplified rectangular rule, ignoring end point # corrections): expr = A[i]**2*psi_ho(A[i])*psi[i]*step if n == 0: print("Setting up binary integrators for the integral:") pprint(Integral(x**2*psi_ho(x)*Function('psi')(x), (x, 0, oo))) # Autowrap it. For functions that take more than one argument, it is # a good idea to use the 'args' keyword so that you know the signature # of the wrapped function. (The dimension m will be an optional # argument, but it must be present in the args list.) binary_integrator[n] = autowrap(expr, args=[A.label, psi.label, step, m]) # Lets see how it converges with the grid dimension print("Checking convergence of integrator for n = %i" % n) for g in range(3, 8): grid, step = np.linspace(0, rmax, 2**g, retstep=True) print("grid dimension %5i, integral = %e" % (2**g, binary_integrator[n](grid, H_ufunc(grid), step))) print("A binary integrator has been set up for each basis state") print("We will now use them to reconstruct a hydrogen solution.") # Note: We didn't need to specify grid or use gridsize before now grid, stepsize = np.linspace(0, rmax, gridsize, retstep=True) print("Calculating coefficients with gridsize = %i and stepsize %f" % ( len(grid), stepsize)) coeffs = {} for n in range(basis_dimension): coeffs[n] = binary_integrator[n](grid, H_ufunc(grid), stepsize) print("c(%i) = %e" % (n, coeffs[n])) print("Constructing the approximate hydrogen wave") hydro_approx = 0 all_steps = {} for n in range(basis_dimension): hydro_approx += basis_ho[n](grid)*coeffs[n] all_steps[n] = hydro_approx.copy() if pylab: line = pylab.plot(grid, all_steps[n], ':', label='max n = %i' % n) # check error numerically diff = np.max(np.abs(hydro_approx - H_ufunc(grid))) print("Error estimate: the element with largest deviation misses by %f" % diff) if diff > 0.01: print("This is much, try to increase the basis size or adjust omega") else: print("Ah, that's a pretty good approximation!") # Check visually if pylab: print("Here's a plot showing the contribution for each n") line[0].set_linestyle('-') pylab.plot(grid, H_ufunc(grid), 'r-', label='exact') pylab.legend() pylab.show() print("""Note: These binary integrators were specialized to find coefficients for a harmonic oscillator basis, but they can process any wave function as long as it is available as a vector and defined on a grid with equidistant points. That is, on any grid you get from numpy.linspace. To make the integrators even more flexible, you can setup the harmonic oscillator solutions with symbolic parameters omega and l. Then the autowrapped binary routine will take these scalar variables as arguments, so that the integrators can find coefficients for *any* isotropic harmonic oscillator basis. """)
def __call__(self, x, c, eps=None, diff=None): ''' Evaluates the RBF Parameters ---------- x : (N,D) array evaluation points c : (M,D) array RBF centers eps : (M,) array, optional shape parameters for each RBF. Defaults to 1.0 diff : (D,) int array, optional Tuple indicating the derivative order for each spatial dimension. For example, if there are three spatial dimensions then providing (2,0,1) would return the RBF after differentiating it twice along the first axis and once along the third axis. Returns ------- out : (N,M) array Returns the RBFs with centers *c* evaluated at *x* Notes ----- This function evaluates the RBF and its derivatives symbolically using sympy and then the symbolic expression is converted to a numerical function. The numerical function is cached and then reused when this function is called multiple times with the same derivative specification. ''' x = np.asarray(x, dtype=float) c = np.asarray(c, dtype=float) if eps is None: eps = np.ones(c.shape[0], dtype=float) else: eps = np.asarray(eps, dtype=float) if diff is None: diff = (0, ) * x.shape[1] else: # make sure diff is immutable diff = tuple(diff) # make sure the input arguments have the proper dimensions if not ((x.ndim == 2) & (c.ndim == 2)): raise ValueError('x and c must be two-dimensional arrays') if not (x.shape[1] == c.shape[1]): raise ValueError( 'x and c must have the same number of spatial dimensions') if not ((eps.ndim == 1) & (eps.shape[0] == c.shape[0])): raise ValueError( 'eps must be a one-dimensional array with length equal to ' 'the number of rows in c') if not (len(diff) == x.shape[1]): raise ValueError( 'diff must have the same length as the number of spatial ' 'dimensions in x and c') # expand to allow for broadcasting x = x[:, None, :] c = c[None, :, :] # this does the same thing as np.rollaxis(x,-1) but is much faster x = np.einsum('ijk->kij', x) c = np.einsum('ijk->kij', c) # add function to cache if not already if diff not in self.cache: dim = len(diff) c_sym = sympy.symbols('c:%s' % dim) x_sym = sympy.symbols('x:%s' % dim) r_sym = sympy.sqrt( sum((x_sym[i] - c_sym[i])**2 for i in range(dim))) expr = self.expr.subs(_R, r_sym) for direction, order in enumerate(diff): if order == 0: continue expr = expr.diff(*(x_sym[direction], ) * order) if _SYM_TO_NUM == 'numpy': func = sympy.lambdify(x_sym + c_sym + (_EPS, ), expr, 'numpy') func = _check_lambdified_output(func) self.cache[diff] = func elif _SYM_TO_NUM == 'cython': func = ufuncify(x_sym + c_sym + (_EPS, ), expr) self.cache[diff] = func args = (tuple(x) + tuple(c) + (eps, )) return self.cache[diff](*args)
from sympy.utilities.autowrap import ufuncify from sympy.utilities.lambdify import lambdify print __file__ NExpr = 10 f_exprs = [] for i in range(0, NExpr): expr = reduce(lambda a, b: a + b, [1.0 / factorial(j) * x**j for j in range(0, i + 1)]) f_exprs.append(expr) print expr ts = time.time() g1 = map(lambda a: ufuncify([x], a), f_exprs) te = time.time() print g1 print "sympy ufuncify compile time: ", (te - ts) print "sympy ufuncify eval value= " for g in g1: print g(0.1) ts = time.time() #g2 = map(lambda a:lambdify([x], a, modules='math'), f_exprs) #11.0926191807 #g2 = map(lambda a:lambdify([x], a, modules='mpmath'), f_exprs) #11.0835108757 g2 = map(lambda a: lambdify([x], a, modules='numpy'), f_exprs) #10.9833340645 #g2 = map(lambda a:lambdify([x], a, modules='numexpr'), f_exprs) #73.1567411423 #g2 = map(lambda a:lambdify([x], a, modules='sympy'), f_exprs) #11.0603320599 te = time.time() print g2
def init(self, variables): sympy_variables = [sympy.Symbol(var.name) for var in variables] self.func = ufuncify(sympy_variables, self.term_string)
def main(): print(__doc__) # arrays are represented with IndexedBase, indices with Idx m = Symbol("m", integer=True) i = Idx("i", m) A = IndexedBase("A") B = IndexedBase("B") x = Symbol("x") print("Compiling ufuncs for radial harmonic oscillator solutions") # setup a basis of ho-solutions (for l=0) basis_ho = {} for n in range(basis_dimension): # Setup the radial ho solution for this n expr = R_nl(n, orbital_momentum_l, omega2, x) # Reduce the number of operations in the expression by eval to float expr = expr.evalf(15) print("The h.o. wave function with l = %i and n = %i is" % (orbital_momentum_l, n)) pprint(expr) # implement, compile and wrap it as a ufunc basis_ho[n] = ufuncify(x, expr) # now let's see if we can express a hydrogen radial wave in terms of # the ho basis. Here's the solution we will approximate: H_ufunc = ufuncify(x, hydro_nl(hydrogen_n, orbital_momentum_l, 1, x)) # The transformation to a different basis can be written like this, # # psi(r) = sum_i c(i) phi_i(r) # # where psi(r) is the hydrogen solution, phi_i(r) are the H.O. solutions # and c(i) are scalar coefficients. # # So in order to express a hydrogen solution in terms of the H.O. basis, we # need to determine the coefficients c(i). In position space, it means # that we need to evaluate an integral: # # psi(r) = sum_i Integral(R**2*conj(phi(R))*psi(R), (R, 0, oo)) phi_i(r) # # To calculate the integral with autowrap, we notice that it contains an # element-wise sum over all vectors. Using the Indexed class, it is # possible to generate autowrapped functions that perform summations in # the low-level code. (In fact, summations are very easy to create, and as # we will see it is often necessary to take extra steps in order to avoid # them.) # we need one integration ufunc for each wave function in the h.o. basis binary_integrator = {} for n in range(basis_dimension): # # setup basis wave functions # # To get inline expressions in the low level code, we attach the # wave function expressions to a regular SymPy function using the # implemented_function utility. This is an extra step needed to avoid # erroneous summations in the wave function expressions. # # Such function objects carry around the expression they represent, # but the expression is not exposed unless explicit measures are taken. # The benefit is that the routines that searches for repeated indices # in order to make contractions will not search through the wave # function expression. psi_ho = implemented_function( "psi_ho", Lambda(x, R_nl(n, orbital_momentum_l, omega2, x))) # We represent the hydrogen function by an array which will be an input # argument to the binary routine. This will let the integrators find # h.o. basis coefficients for any wave function we throw at them. psi = IndexedBase("psi") # # setup expression for the integration # step = Symbol("step") # use symbolic stepsize for flexibility # let i represent an index of the grid array, and let A represent the # grid array. Then we can approximate the integral by a sum over the # following expression (simplified rectangular rule, ignoring end point # corrections): expr = A[i]**2 * psi_ho(A[i]) * psi[i] * step if n == 0: print("Setting up binary integrators for the integral:") pprint(Integral(x**2 * psi_ho(x) * Function("psi")(x), (x, 0, oo))) # Autowrap it. For functions that take more than one argument, it is # a good idea to use the 'args' keyword so that you know the signature # of the wrapped function. (The dimension m will be an optional # argument, but it must be present in the args list.) binary_integrator[n] = autowrap(expr, args=[A.label, psi.label, step, m]) # Lets see how it converges with the grid dimension print("Checking convergence of integrator for n = %i" % n) for g in range(3, 8): grid, step = np.linspace(0, rmax, 2**g, retstep=True) print("grid dimension %5i, integral = %e" % (2**g, binary_integrator[n](grid, H_ufunc(grid), step))) print("A binary integrator has been set up for each basis state") print("We will now use them to reconstruct a hydrogen solution.") # Note: We didn't need to specify grid or use gridsize before now grid, stepsize = np.linspace(0, rmax, gridsize, retstep=True) print("Calculating coefficients with gridsize = %i and stepsize %f" % (len(grid), stepsize)) coeffs = {} for n in range(basis_dimension): coeffs[n] = binary_integrator[n](grid, H_ufunc(grid), stepsize) print("c(%i) = %e" % (n, coeffs[n])) print("Constructing the approximate hydrogen wave") hydro_approx = 0 all_steps = {} for n in range(basis_dimension): hydro_approx += basis_ho[n](grid) * coeffs[n] all_steps[n] = hydro_approx.copy() if pylab: line = pylab.plot(grid, all_steps[n], ":", label="max n = %i" % n) # check error numerically diff = np.max(np.abs(hydro_approx - H_ufunc(grid))) print("Error estimate: the element with largest deviation misses by %f" % diff) if diff > 0.01: print("This is much, try to increase the basis size or adjust omega") else: print("Ah, that's a pretty good approximation!") # Check visually if pylab: print("Here's a plot showing the contribution for each n") line[0].set_linestyle("-") pylab.plot(grid, H_ufunc(grid), "r-", label="exact") pylab.legend() pylab.show() print("""Note: These binary integrators were specialized to find coefficients for a harmonic oscillator basis, but they can process any wave function as long as it is available as a vector and defined on a grid with equidistant points. That is, on any grid you get from numpy.linspace. To make the integrators even more flexible, you can setup the harmonic oscillator solutions with symbolic parameters omega and l. Then the autowrapped binary routine will take these scalar variables as arguments, so that the integrators can find coefficients for *any* isotropic harmonic oscillator basis. """)
def flowLines(F, Ipb, LAT, flow): f = F[0] ## sympy function, matrix valued. k = len(F[1]) ## number of parameters to f. F[1] is the variable names. ## F[2] is the integration bounds. Df = sp.zeros(3, k) for i in range(k): Df[:, i] = sp.diff(f, F[1][i]) Cf = sp.sqrt((Df.transpose() * Df).det()).simplify() ## Cf is the length/area/volume distortion of f. x, y, z = sp.symbols('x y z') P = sp.Matrix([x, y, z]) ## we have to push I through Df to get the current vector field. I = Df * Ipb den = sp.Matrix(f - P) ITG = I.cross(den) den = den.dot(den)**(3 / 2) ITG = (ITG / den) ## let's make an entire routine that does Euler's method callable? ## this is possibly something to experiment with, the level of abstraction. ## we could also just make the integration command callable. ITGc = [ufuncify(F[1] + [x, y, z], ITG[i, 0]) for i in range(3)] ## compile the curve as well, as we need to check distances C = [ufuncify(F[1], F[0][i, 0]) for i in range(3)] ## loop running through the lattice [X,Y,Z]=LAT computing flowlines at those ## coordinates. retval = [] fp = FloatProgress(min=0, max=100, description="Flowlines") display(fp) ## progrss indicator count = 0 totc = LAT[0].shape[0] * LAT[0].shape[1] * LAT[0].shape[2] for i, j, k in it.product(range(LAT[0].shape[0]), range(LAT[0].shape[1]), range(LAT[0].shape[2])): # TODO: let's throw in a basic check to see if this starting coordinate # is too close to the curve. errFlag = False flowline = [[LAT[0][i, j, k]], [LAT[1][i, j, k]], [LAT[2][i, j, k]]] for s in range(flow[1]): dp = [ INT.nquad(ITGc[l], F[2], args=(flowline[0][-1], flowline[1][-1], flowline[2][-1]), opts=[{ 'epsrel': 0 }, { 'epsabs': 1e-3 }])[0] for l in range(3) ] ## here is a primitive check to see if our lattice point is too close to ## the curve. Should be replaced with something more sophisticated... later. ldp = np.sqrt(sum([crd**2 for crd in dp])) if (ldp > 1e+8): errFlag = True break ## assemble our flowline for l in range(3): flowline[l].append(flowline[l][-1] + flow[0] * dp[l] / ldp) count += 1 fp.value = int(100 * count / totc) ## let's not build the flow line if ldp is too large. if errFlag == False: retval.append(flowline) fp.close() return retval, C
def generate_k_func_4wv(pols=(-1,-1,-1,-1), n_symb = None): ''' Generates two functions that should sum to zero when the phase and frequency matching conditions are satisfied. Args: pols (optional[tuple]): list of polarizations for the four freqs. n_sym (optional[function]): index of refraction as a function of nu. Returns: diff_func_4wv_1 (function): a function of only two variables phi1 and phi2 diff_func_4wv_2 (function): a function of only two variables phi1 and nu3 ''' ## from http://refractiveindex.info/?shelf=main&book=LiNbO3&page=Zelmon-o lambd,nu,nu1,nu2,nu3,nu4 = sp.symbols( 'lambda nu nu_1 nu_2 nu_3 nu_4') l2 = lambd **2 if n_symb is None: def n_symb(pol=1): s = 1. if pol == 1: s += 2.6734 * l2 / (l2 - 0.01764) s += 1.2290 * l2 / (l2 - 0.05914) s += 12.614 * l2 / (l2 - 474.6) else: # pol = -1 s += 2.9804 * l2 / (l2 - 0.02047) s += 0.5981 * l2 / (l2 - 0.0666) s += 8.9543 * l2 / (l2 - 416.08) return sp.sqrt(s) def k_symb(symbol=nu,pol=1): ''' k is accurate for nu inputs between 6-60. ''' return ((n_symb(pol=pol) * symbol ) .subs(lambd,scipy.constants.c / (symbol*1e7))) ## / scipy.constants.c phi1, phi2 = sp.symbols('phi_1 phi_2') ex1 = ( (k_symb(nu1,pol=pols[0]) +k_symb(nu2,pol=pols[1])) .expand().subs({nu1:(phi1 + phi2)/2, nu2: (phi1-phi2)/2}) ) ex2 = -((k_symb(nu3,pol=pols[2]) +k_symb(nu4,pol=pols[3])) .expand().subs(nu4,-phi1-nu3) ) diff_func_4wv_1 = ufuncify([phi1,phi2], ex1) diff_func_4wv_2 = ufuncify([phi1,nu3], ex2) def diff_func_4wv_1_ranges_checked(phi1,phi2): nus = [phi1 + phi2, phi1 - phi2] if any([abs(nu) < 6. or abs(nu) > 60. for nu in nus]): return float('NaN') else: return diff_func_4wv_1(phi1,phi2) def diff_func_4wv_2_ranges_checked(phi1,nu3): if abs(phi1) < 6. or abs(phi1) > 60.: return float('NaN') else: return diff_func_4wv_2(phi1,nu3) return diff_func_4wv_1_ranges_checked, diff_func_4wv_2_ranges_checked
def __call__(self,x,c,eps=None,diff=None): ''' evaluates M radial basis functions (RBFs) at N points. Parameters ---------- x : (N,D) array evaluate the RBFs at these positions c : (M,D) array centers for each RBF eps : (M,) array, optional shape parameters for each RBF. Defaults to 1.0 diff : (D,) int array, optional a tuple whos length is equal to the number of spatial dimensions. Each value in the tuple must be an integer indicating the order of the derivative in that spatial dimension. For example, if the the spatial dimensions of the problem are 3 then diff=(2,0,1) would compute the second derivative in the first dimension then the first derivative in the third dimension. In other words, it would compute the d^3u/dx^2*dz, where x and z are the first and third spatial dimension and u is the RBF Returns ------- out : (N,M) array alternant matrix consisting of each RBF evaluated at x Note ---- the derivatives are computed symbolically in Sympy and then lambdified to evaluate the expression with the provided values. The lambdified functions are cached in the scope of the radial module and will be recalled if a value for diff is used more than once in the Python session. ''' x = np.asarray(x,dtype=float) c = np.asarray(c,dtype=float) if eps is None: eps = np.ones(c.shape[0]) else: eps = np.asarray(eps,dtype=float) if diff is None: diff = (0,)*x.shape[1] else: # make sure diff is immutable diff = tuple(diff) # make sure the input arguments have the proper dimensions if not ((x.ndim == 2) & (c.ndim == 2)): raise ValueError( 'x and c must be two-dimensional arrays') if not (x.shape[1] == c.shape[1]): raise ValueError( 'x and c must have the same number of spatial dimensions') if x.shape[1] == 0: raise ValueError( 'spatial dimensions of x and c must be at least one') if not ((eps.ndim == 1) & (eps.shape[0] == c.shape[0])): raise ValueError( 'eps must be a one-dimensional array with length equal to ' 'the number of rows in c') if not (len(diff) == x.shape[1]): raise ValueError( 'diff must have the same length as the number of spatial ' 'dimensions in x and c') # expand to allow for broadcasting x = x[:,None,:] c = c[None,:,:] # this does the same thing as np.rollaxis(x,-1) but is much faster x = np.einsum('ijk->kij',x) c = np.einsum('ijk->kij',c) # add function to cache if not already if diff not in self.cache: dim = len(diff) c_sym = sympy.symbols('c:%s' % dim) x_sym = sympy.symbols('x:%s' % dim) r_sym = sympy.sqrt(sum((x_sym[i]-c_sym[i])**2 for i in range(dim))) expr = self.expr.subs(_R,r_sym) for direction,order in enumerate(diff): if order == 0: continue expr = expr.diff(*(x_sym[direction],)*order) if _SYM_TO_NUM == 'numpy': func = sympy.lambdify(x_sym+c_sym+(_EPS,),expr,'numpy') func = _check_lambdified_output(func) self.cache[diff] = func elif _SYM_TO_NUM == 'cython': func = ufuncify(x_sym+c_sym+(_EPS,),expr) self.cache[diff] = func args = (tuple(x)+tuple(c)+(eps,)) return self.cache[diff](*args)