def test_solveset(): x = Symbol('x') A = Matrix([[x, 2, x * x], [4, 5, x], [x, 8, 9]]) solns = solve(det(A), x) solns_set = list(solveset(det(A), x)) print(solns) print('\n') print(solns_set) print('\n\n\n') print((solns[0])) print('\n') print((solns_set[0])) soln_sub = solns[0].subs(x, 1) solnset_sub = solns_set[0].subs(x, 1) s1 = soln_sub.evalf() s1set = solnset_sub.evalf() s2set = solns_set[1].subs(x, 1).evalf() print(s1) print(s1set) print(s2set)
def gauss_neg_log_dens(pdf, var_and_param_names_and_values={}, **kw_var_and_param_names_and_values): var_and_param_names_and_values = combine_dict_and_kwargs(var_and_param_names_and_values, kw_var_and_param_names_and_values) if not pdf.PreProcessed: pdf.preprocess() if pdf.LogDetCov is None: neg_log_dens = (pdf.NumDims * log(2 * pi) + log(det(pdf.Cov)) + det(pdf.DemeanedVarVector * pdf.Cov.inverse() * pdf.DemeanedVarVector.T)) / 2 else: neg_log_dens = (pdf.NumDims * log(2 * pi) + pdf.LogDetCov + det(pdf.DemeanedVarVector * Matrix(pdf.InvCov) * pdf.DemeanedVarVector.T)) / 2 return sympy_xreplace(neg_log_dens, var_and_param_names_and_values)
def test_dft(): n, i, j = symbols('n i j') assert DFT(4).shape == (4, 4) assert ask(Q.unitary(DFT(4))) assert Abs(simplify(det(Matrix(DFT(4))))) == 1 assert DFT(n) * IDFT(n) == Identity(n) assert DFT(n)[i, j] == exp(-2 * S.Pi * I / n)**(i * j) / sqrt(n)
def cross(self, other): """ Returns the cross product of this Vector with another Vector or Dyadic instance. The cross product is a Vector, if 'other' is a Vector. If 'other' is a Dyadic, this returns a Dyadic instance. Parameters ========== other: Vector/Dyadic The Vector or Dyadic we are crossing with. Examples ======== >>> from sympy.vector import CoordSys3D >>> C = CoordSys3D('C') >>> C.i.cross(C.j) C.k >>> C.i ^ C.i 0 >>> v = 3*C.i + 4*C.j + 5*C.k >>> v ^ C.i 5*C.j + (-4)*C.k >>> d = C.i.outer(C.i) >>> C.j.cross(d) (-1)*(C.k|C.i) """ # Check special cases if isinstance(other, Dyadic): if isinstance(self, VectorZero): return Dyadic.zero outdyad = Dyadic.zero for k, v in other.components.items(): cross_product = self.cross(k.args[0]) outer = cross_product.outer(k.args[1]) outdyad += v * outer return outdyad elif not isinstance(other, Vector): raise TypeError(str(other) + " is not a vector") elif (isinstance(self, VectorZero) or isinstance(other, VectorZero)): return Vector.zero outvec = Vector.zero for system, vect in other.separate().items(): tempm = [[system.i, system.j, system.k], [self & system.i, self & system.j, self & system.k], [vect & system.i, vect & system.j, vect & system.k]] outvec += det(Matrix(tempm)) return outvec
def aff2trpzs(affmat): tvec = affmat[:-1,-1] rpzsmat = affmat[:-1,:-1] zsmat = (rpzsmat.T*rpzsmat).cholesky().T zfac = Mat([zsmat[0,0], zsmat[1,1], zsmat[2,2]]) zmat = zfac2zmat(zfac) smat = zmat.inv() * zsmat stri = smat2stri(smat) rpmat = rpzsmat * zsmat.inv() parity = det(rpmat) rquat = rmat2rquat(rpmat*parity) return tvec, rquat, parity, zfac, stri
def solve_diel(chiL, chiR, chiZ, THETA, Bfield, verbose=False, force_numeric=False, use_old_method=False): ''' Solves the wave equation to find the two propagating normal modes of the system, for a given magnetic field angle THETA. For the general case, use symbolic python to solve for the roots of n-squared. (Escapes this slow approach for the two analytic cases for the Voigt and Faraday geometries) Returns the rotation matrix to transform the coordinate system into the normal mode basis, and returns the two refractive index arrays. Inputs: chiL, chiR, chiZ : 1D lists or numpy arrays, of length N, that are the frequency-dependent electric susceptibilities THETA : Float, Magnetic field angle in radians Bfield : Float, Magnitude of applied magnetic field (skips slow approach if magnetic field is very close to zero) Options: verbose : Boolean to output more print statements (timing reports mostly) force_numeric : If True, forces all angles to go through the numeric approach, rather than escaping for the analytic cases (THETA=0, THETA=pi/2...) use_old_method : If True, forces use of numerical method rather than analytical method. Outputs: RotMat : Rotation matrix to transform coordinate system, dimensions (3, 3, N) n1 : First solution for refractive index, dimensions (N) n2 : Second solution for refractive index, dimensions (N) ''' approx_threshold = 1e-4 ##Value used do determine if THETA is close enough ##to Voigt or Faraday geometries to use an approximation if verbose: print(('B-field angle (rad, pi rad): ', THETA, THETA / np.pi)) stt = time.clock() # make chiL,R,Z arrays if not already chiL = np.array(chiL) chiR = np.array(chiR) chiZ = np.array(chiZ) #verbose=True #### Escape the slow loop for analytic (Faraday and Voigt) cases ## For these analytic cases we can use array operations and it is therefore ## much faster to compute if (abs(THETA % (2 * np.pi) - np.pi / 2) < approx_threshold) or (abs(THETA % (2 * np.pi) - 3 * np.pi / 2) < approx_threshold) and (not force_numeric): # ANALYTIC SOLNS FOR VOIGT if verbose: print('Voigt - analytic') # solutions for elements of the dielectric tensor: ex = 0.5 * (2. + chiL + chiR) exy = 0.5j * (chiR - chiL) ez = 1.0 + chiZ # refractive indices to propagate n1 = np.sqrt(ex + exy**2 / ex) n2 = np.sqrt(ez) #ev1 = [np.zeros(len(ex)),ex/exy,np.ones(len(ex))] #ev2 = [np.ones(len(ex)),np.zeros(len(ex)),np.zeros(len(ex))] #ev3 = [np.zeros(len(ex)),np.zeros(len(ex)),np.ones(len(ex))] #RotMat = np.array([ev1,ev2,ev3]) ones = np.ones(len(ex)) zeros = np.zeros(len(ex)) # *Changed output to new array format RotMat = np.array([[zeros, ones, zeros], [ex / exy, zeros, zeros], [ones, zeros, ones]]).T if verbose: print('Shortcut:') print((RotMat.shape)) print((n1.shape)) print((n2.shape)) elif ((abs(THETA) < approx_threshold) or ((abs(THETA - np.pi)) < approx_threshold) or abs(Bfield) < 1e-2 ) and (not force_numeric ): ## Use Faraday geometry if Bfield is very close to zero # ANALYTIC SOLNS FOR FARADAY #if verbose: if verbose: print('Faraday - analytic TT') ex = 0.5 * (2. + chiL + chiR) exy = 0.5j * (chiR - chiL) e_z = 1.0 + chiZ n1 = np.sqrt(ex + 1.j * exy) n2 = np.sqrt(ex - 1.j * exy) #ev1 = np.array([-1.j*np.ones(len(ex)),np.ones(len(ex)),np.zeros(len(ex))]) #ev2 = np.array([1.j*np.ones(len(ex)),np.ones(len(ex)),np.zeros(len(ex))]) #ev3 = [np.zeros(len(ex)),np.zeros(len(ex)),np.ones(len(ex))] ones = np.ones(len(ex)) zeros = np.zeros(len(ex)) # *Changed output to new array format if (abs(THETA) < approx_threshold): #RotMat = np.array([ev1,ev2,ev3]) RotMat = np.array([[-1.j * ones, 1.j * ones, zeros], [ones, ones, zeros], [zeros, zeros, ones]]).T else: #if anti-aligned, swap the two eigenvectors #RotMat = np.array([ev2,ev1,ev3]) RotMat = np.array([[1.j * ones, -1.j * ones, zeros], [ones, ones, zeros], [zeros, zeros, ones]]).T if verbose: print('Shortcut:') print((RotMat.shape)) print((n1.shape)) print((n2.shape)) elif use_old_method: print("using old method") if verbose: print('Non-analytic angle.. This will take a while...' ) ##### THIS IS THE ONE THAT's WRONG.... # set up sympy symbols theta = Symbol('theta', real=True) n_sq = Symbol('n_sq') e_x = Symbol('e_x') e_xy = Symbol('e_xy') e_z = Symbol('e_z') # General form of the dielectric tensor DielMat = Matrix(([(e_x - n_sq) * cos(theta), e_xy, e_x * sin(theta)], [-e_xy * cos(theta), e_x - n_sq, -e_xy * sin(theta)], [(n_sq - e_z) * sin(theta), 0, e_z * cos(theta)])) et1 = time.clock() - stt # Substitute in angle DielMat_sub = DielMat.subs(theta, pi * THETA / np.pi) et2 = time.clock() - stt # Find solutions for complex indices for a given angle solns = solve(det(DielMat_sub), n_sq) et3a = time.clock() - stt #print et3a # Find first refractive index DielMat_sub1 = DielMat_sub.subs(n_sq, solns[0]) n1 = np.zeros(len(chiL), dtype='complex') n1old = np.zeros(len(chiL), dtype='complex') # Find second refractive index DielMat_sub2 = DielMat_sub.subs(n_sq, solns[1]) n2 = np.zeros(len(chiL), dtype='complex') n2old = np.zeros(len(chiL), dtype='complex') et3b = time.clock() - stt Dsub1 = lambdify((e_x, e_xy, e_z), DielMat_sub1, 'numpy') Dsub2 = lambdify((e_x, e_xy, e_z), DielMat_sub2, 'numpy') nsub1 = lambdify((e_x, e_xy, e_z), solns[0], 'numpy') nsub2 = lambdify((e_x, e_xy, e_z), solns[1], 'numpy') # Initialise rotation matrix RotMat = np.zeros((3, 3, len(chiL)), dtype='complex') et3c = time.clock() - stt # populate refractive index arrays n1 = np.sqrt( nsub1(0.5 * (2. + chiL + chiR), 0.5j * (chiR - chiL), (1.0 + chiZ))) n2 = np.sqrt( nsub2(0.5 * (2. + chiL + chiR), 0.5j * (chiR - chiL), (1.0 + chiZ))) et3 = time.clock() - stt if verbose: print(('setup time:', et1, et1)) print(('solve nsq: (total/solve/sub in) ', et3a, et3a - et2, et2 - et1)) print(( 'get nsq arrays (tot time / populate ref. index / gen. lambdify / sub in): ', et3, et3 - et3c, et3c - et3b, et3b - et3a)) # loop over all elements of chiL,R,Z to populate eigenvectors # time-limiting step for arrays of length >~ 5000 for i, (cL, cR, cZ) in enumerate(zip(chiL, chiR, chiZ)): #if verbose: print 'Detuning point i: ',i #time diagnostics st = time.clock() ''' ## OLD and slow method:: # Sub in values of susceptibility DielMat_sub1a = DielMat_sub1.subs(e_x, 0.5*(2.+cL+cR)) DielMat_sub1a = DielMat_sub1a.subs(e_xy, 0.5j*(cR-cL)) DielMat_sub1a = DielMat_sub1a.subs(e_z, (1.0+cZ)) et1 = time.clock() - st # Evaluate and convert to numpy array DM = np.array(DielMat_sub1a.evalf()) DMa = np.zeros((3,3),dtype='complex') for ii in range(3): for jj in range(3): DMa[ii,jj] = np.complex128(DM[ii,jj]) et2 = time.clock() - st # use scipy to find eigenvector #ev1 = Matrix(DMa).nullspace() #print 'Sympy: ', ev1 ev1old = nullOld(DMa).T[0] #ev1 = null(DMaNP).T # sub in for ref. index n1soln = solns[0].subs(e_x, 0.5*(2.+cL+cR)) n1soln = n1soln.subs(e_xy, 0.5j*(cR-cL)) n1soln = n1soln.subs(e_z, (1.0+cZ)) # Populate the refractive index array n1old[i] = np.sqrt(np.complex128(n1soln.evalf())) ## /OLD method ''' # NEW method # Sub in values of susceptibility DMaNP = Dsub1(0.5 * (2. + cL + cR), 0.5j * (cR - cL), (1.0 + cZ)) #print DMa ev1 = null(DMaNP).T # Populate the refractive index array #n1[i] = np.sqrt(nsub1(0.5*(2.+cL+cR), 0.5j*(cR-cL), (1.0+cZ))) ''' ## METHOD COMPARISON print 'SymPy:' print DMa print DMa.shape, type(DMa) print 'Numpy' print DMaNP print DMaNP.shape, type(DMaNP) print 'Eigenvectors ...' print 'Old: ', ev1old print 'New: ',ev1 ''' #print '\n\n\n' #print 'scipy: ', ev1 et3 = time.clock() - st et4 = time.clock() - st # ## Now repeat the above for second eigenvector # ## NEW # Sub in values of susceptibility DMaNP = Dsub2(0.5 * (2. + cL + cR), 0.5j * (cR - cL), (1.0 + cZ)) # Find null eigenvector ev2 = null(DMaNP).T # Populate the refractive index array #n2[i] = np.sqrt(nsub2(0.5*(2.+cL+cR), 0.5j*(cR-cL), (1.0+cZ))) et5 = time.clock() - st ''' ## OLD # Evaluate and convert to numpy array DielMat_sub2a = DielMat_sub2.subs(e_x, 0.5*(2.+cL+cR)) DielMat_sub2a = DielMat_sub2a.subs(e_xy, 0.5j*(cR-cL)) DielMat_sub2a = DielMat_sub2a.subs(e_z, (1.0+cZ)) DM = np.array(DielMat_sub2a.evalf()) DMa = np.zeros((3,3),dtype='complex') for ii in range(3): for jj in range(3): DMa[ii,jj] = np.complex128(DM[ii,jj]) et6 = time.clock() - st # use scipy to find eigenvector ev2old = nullOld(DMa).T[0] et7 = time.clock() - st # sub in for ref. index n2soln = solns[1].subs(e_x, 0.5*(2.+cL+cR)) n2soln = n2soln.subs(e_xy, 0.5j*(cR-cL)) n2soln = n2soln.subs(e_z, (1.0+cZ)) # Populate the refractive index array n2old[i] = np.sqrt(np.complex128(n2soln.evalf())) ''' # Populate the rotation matrix RotMat[:, :, i] = [ev1, ev2, [0, 0, 1]] et_tot = time.clock() - stt if verbose: print(('Time elapsed (non-analytic angle):', et_tot)) else: if verbose: print("Using analytical method") #Uses analytical method n1, n2, RotMat = non_standard_n1_n2_RotMat(chiL, chiR, chiZ, THETA) #print("RotMat =", RotMat) #print("n1 = ", n1) #print("n2 = ", n2, '\n') if verbose: print('SD done') return RotMat, n1, n2
def diverg(X, args, g=None): """Return the divergence of a vector field X. Compute divergence of vector field consisting of N elements. Examples: ========= >>> from tensor_analysis.tensor_fields import diverg >>> from sympy import symbols, cos >>> from sympy.matrices import Matrix >>> x1, x2, x3 = symbols('x1 x2 x3') X is a vector field, args it's a list of symbol arguments of the vector field X. It's can be in list, array of arraypy or contravariant tensor: >>> X = [x1*x2**3,x2-cos(x3),x3**3-x1] >>> arg = [x1, x2, x3] g - optional parameter, metric tensor, which can be a matrix "Matrix", array of arraypy or covariant tensor: >>> g = Matrix([[2,1,0],[1,3,0],[0,0,1]]) >>> dv = diverg(X,arg,g) >>> print(dv) x2**3 + 3*x3**2 + 1 """ # Handling of a vector of arguments check_vector_of_arguments(args) if isinstance(args, list): idx_args = 0 else: idx_args = args.start_index[0] # Handling of the first vector field check_the_vector_field(X) if isinstance(X, (TensorArray, Arraypy)): idx_X = X.start_index[0] else: idx_X = 0 if idx_args != idx_X: raise ValueError( "The start index of vector field and vector of arguments must be \ equal") # Handling of the metric tensor if g is not None: if isinstance(g, (TensorArray, Arraypy)): g = g.to_matrix() else: g = eye(len(args)) # Calculation sq = sqrt(abs(det(g))) diver = 0 for k in range(len(args)): diver += simplify(1 / sq * sum([diff(X[k + idx_X] * sq, args[k + idx_X])])) # Output return diver
def hodge_star(T, g): """The calculation actions on the forms of the Hodge operator's. Examples: ========= >>> from sympy import symbols, Matrix >>> from tensor_analysis.arraypy import Arraypy, TensorArray >>> from tensor_analysis.tensor_fields import hodge_star >>> x1, x2, x3, x4 = symbols('x1 x2 x3 x4') >>> y2 = TensorArray(Arraypy((3, 3)), (-1, -1)) >>> y2[0, 1] = x3*x4 >>> y2[0, 2] = -x2**3 >>> y2[1, 0] = -x3*x4 >>> y2[1, 2] = x1*x2 >>> y2[2, 0] = x2**3 >>> y2[2, 1] = -x1*x2 >>> g = Matrix([[1,3,0],[-3,1,0],[0,0,1]]) >>> print(hodge_star(y2,g)) sqrt(10)*x1*x2/5 - 3*sqrt(10)*x2**3/5 3*sqrt(10)*x1*x2/5 + sqrt(10)*x2**3/5 sqrt(10)*x3*x4/5 """ if not isinstance(T, (TensorArray)): raise ValueError( "The type of tensor must be TensorArray") if (len(T.type_pq) == 1 and T.type_pq[0] != 1) or (len(T.type_pq) > 1 and T.type_pq[0] != 0): raise ValueError("The valency of tensor must be (0,q)") if T.rank > 1: if not is_asymmetric(T): raise ValueError("The tensor must be a skew-symmetric") # Handling of the metric tensor check_metric_tensor(g) if isinstance(g, (TensorArray, Arraypy)): idx_start_g = g.start_index[0] det_g = det(g.to_matrix()) else: idx_start_g = 0 det_g = det(g) g = matrix2tensor(g) # The definition of the start index idx_start_T = T.start_index[0] if idx_start_T != idx_start_g: raise ValueError( "The start index of the tensor and metric must be equal") # 1. Calculating of tensor mu n = T.shape[0] # the dimension of the input array k = T.rank sqrt_det_g = simplify(sqrt(abs(det_g))) valence_list_mu = [(-1) for i in range(n)] mu = Arraypy([n, n, idx_start_g]).to_tensor(valence_list_mu) for idx in mu.index_list: mu[idx] = simplify(sqrt_det_g * sign_permutations(list(idx))) # 2. Tensor product mu and T uT = tensor_product(mu, T) # 3.Convolution by the first k-index and the last k-index # low_idx_numbers it is a list with the positions on which are the lower # indices low_idx_numbers = [i + 1 for i in range(k)] # Upping of the first k-lower indices of a tensor for position in low_idx_numbers: uT = raise_index(uT, g, position) # Convolution kn = n + 1 for i in range(k): uT = uT.contract(1, kn) kn = kn - 1 return uT
def test_dft(): assert DFT(4).shape == (4, 4) assert ask(Q.unitary(DFT(4))) assert Abs(simplify(det(Matrix(DFT(4))))) == 1 assert DFT(n)*IDFT(n) == Identity(n) assert DFT(n)[i, j] == exp(-2*S.Pi*I/n)**(i*j) / sqrt(n)
def FindScalarCurvature(reset=True): global x,X,ginv global christoffel1, christoffel2, riemanncurvature, riccitensor if reset: christoffel1 = {} christoffel2 = {} riemanncurvature = {} riccitensor = {} a = 1 if a==0: x = ["t","x"] dx = ["dt","dx"] n = len(x) [t,x1] = list(map(Symbol,x)) Xs = ["f1","f2"] [f1,f2] = list(map(Function,Xs)) X = [f1(t,x1), f2(t,x1)] X = [cos(t),x1**2*t] elif a==1: x = ["r","theta","phi"] dx = ["dr","dtheta","dphi"] n = len(x) [x1,x2,x3] = list(map(Symbol,x)) X = [x1*sin(x2)*cos(x3),x1*sin(x2)*sin(x3),x1*cos(x2)] print("\nX=%s" % (str(X))) N = len(X) J = zeros( len(x), len(X)) for i in range(N): for j in range(n): J[i,j] = X[i].diff(Symbol(x[j])) J = Matrix(J) for i in range(n**2): L = Base(i,n,2) a = L[0] b = L[1] g = (J.T)*(J) for i in range(N): for j in range(n): g[i,j] = g[i,j].simplify() s = "\nCalculating with metric tensor g_ij = \n" print(s) ginv = g.inv() for i in range(n**2): L = Base(i,n,2) a = L[0] b = L[1] s = "%s%s: %s" % (str(a),str(b),str(g[a,b])) print(s) print("\n(Christoffel Symbols 1st) \n\nGamma1_ijk = ", end=' ') for i in range(n**3): L = Base(i,n,3) c = L[0] a = L[1] b = L[2] print(str(c)+str(a)+str(b)+":",Christoffel1(g,c,a,b),",", end=' ') print("\n\n(Christoffel Symbols 2nd) Gamma2_Ijk = ", end=' ') for i in range(n**3): L = Base(i,n,3) c = L[0] a = L[1] b = L[2] print(str(c)+str(a)+str(b)+":",Christoffel2(g,c,a,b),",", end=' ') dA = sqrt(det(g)) for j in range(n): dA *= Symbol(dx[j]) s = "\ndA = sqrt(det(g[i,j]))*dx*dy = %s" % (str(dA)) print(s) s = "\n\nds**2 = %s" % str(LineElement(g,dx)) print(s) s = "\n\n(Ricci Tensor) R_ij = \n" print(s) for i in range(n**2): L = Base(i,n,2) a = L[0] b = L[1] R2 = RicciTensor(g,a,b).expand().simplify() s = "%s%s: %s" % (str(a),str(b),str(R2)) print(s) R = ScalarCurvature(g).expand().simplify() s = "\n\nScalar Curvature R = %s" % str(R) print(s) return
from sympy import symbols, simplify, expand, factor, fraction from sympy.matrices import Matrix, det # %% 2by2 l11, l12, l21, l22 = symbols('l11 l12 l21 l22') l = Matrix(((l11, l12), (l21, l22))) # %% 3by3 (l11, l12, l13, l21, l22, l23, l31, l32, l33) = symbols('\ l11 l12 l13 \ l21 l22 l23 \ l31 l32 l33 \ ') l = Matrix(((l11, l12, l13), (l21, l22, l23), (l31, l32, l33))) # %% compute result = factor((l.T @ l).inv().trace()) num, denom = fraction(result) print('\n\nnum\n----------\n') print(num) print('\n\ndenom\n----------\n') print(denom) print('\n\ndet\n----------\n') print(factor(det(l.T @ l)))
def diverg(X, args, g=None): """Return the divergence of a vector field X. Compute divergence of vector field consisting of N elements. Examples: ========= >>> from sympy.tensor.tensor_fields import diverg >>> from sympy import symbols, cos >>> from sympy.matrices import Matrix >>> x1, x2, x3 = symbols('x1 x2 x3') X is a vector field, args it's a list of symbol arguments of the vector field X. It's can be in list, array of arraypy or contravariant tensor: >>> X = [x1*x2**3,x2-cos(x3),x3**3-x1] >>> arg = [x1, x2, x3] g - optional parameter, metric tensor, which can be a matrix "Matrix", array of arraypy or covariant tensor: >>> g = Matrix([[2,1,0],[1,3,0],[0,0,1]]) >>> dv = diverg(X,arg,g) >>> print(dv) x2**3 + 3*x3**2 + 1 """ # Handling of a vector of arguments check_vector_of_arguments(args) if isinstance(args, list): idx_args = 0 else: idx_args = args.start_index[0] # Handling of the first vector field check_the_vector_field(X) if isinstance(X, (TensorArray, Arraypy)): idx_X = X.start_index[0] else: idx_X = 0 if idx_args != idx_X: raise ValueError( "The start index of vector field and vector of arguments must be \ equal") # Handling of the metric tensor if g is not None: if isinstance(g, (TensorArray, Arraypy)): g = g.to_matrix() else: g = eye(len(args)) # Calculation sq = sqrt(abs(det(g))) diver = 0 for k in range(len(args)): diver += simplify(1 / sq * sum([diff(X[k + idx_X] * sq, args[k + idx_X])])) # Output return diver
def hodge_star(T, g): """The calculation actions on the forms of the Hodge operator's. Examples: ========= >>> from sympy import symbols, Matrix >>> from sympy.tensor.arraypy import Arraypy, TensorArray >>> from sympy.tensor.tensor_fields import hodge_star >>> x1, x2, x3, x4 = symbols('x1 x2 x3 x4') >>> y2 = TensorArray(Arraypy((3, 3)), (-1, -1)) >>> y2[0, 1] = x3*x4 >>> y2[0, 2] = -x2**3 >>> y2[1, 0] = -x3*x4 >>> y2[1, 2] = x1*x2 >>> y2[2, 0] = x2**3 >>> y2[2, 1] = -x1*x2 >>> g = Matrix([[1,3,0],[-3,1,0],[0,0,1]]) >>> print(hodge_star(y2,g)) sqrt(10)*x1*x2/5 - 3*sqrt(10)*x2**3/5 3*sqrt(10)*x1*x2/5 + sqrt(10)*x2**3/5 sqrt(10)*x3*x4/5 """ if not isinstance(T, (TensorArray)): raise ValueError("The type of tensor must be TensorArray") if (len(T.type_pq) == 1 and T.type_pq[0] != 1) or (len(T.type_pq) > 1 and T.type_pq[0] != 0): raise ValueError("The valency of tensor must be (0,q)") if T.rank > 1: if not is_asymmetric(T): raise ValueError("The tensor must be a skew-symmetric") # Handling of the metric tensor check_metric_tensor(g) if isinstance(g, (TensorArray, Arraypy)): idx_start_g = g.start_index[0] det_g = det(g.to_matrix()) else: idx_start_g = 0 det_g = det(g) g = matrix2tensor(g) # The definition of the start index idx_start_T = T.start_index[0] if idx_start_T != idx_start_g: raise ValueError( "The start index of the tensor and metric must be equal") # 1. Calculating of tensor mu n = T.shape[0] # the dimension of the input array k = T.rank sqrt_det_g = simplify(sqrt(abs(det_g))) valence_list_mu = [(-1) for i in range(n)] mu = Arraypy([n, n, idx_start_g]).to_tensor(valence_list_mu) for idx in mu.index_list: mu[idx] = simplify(sqrt_det_g * sign_permutations(list(idx))) # 2. Tensor product mu and T uT = tensor_product(mu, T) # 3.Convolution by the first k-index and the last k-index # low_idx_numbers it is a list with the positions on which are the lower # indices low_idx_numbers = [i + 1 for i in range(k)] # Upping of the first k-lower indices of a tensor for position in low_idx_numbers: uT = raise_index(uT, g, position) # Convolution kn = n + 1 for i in range(k): uT = uT.contract(1, kn) kn = kn - 1 return uT