def as_explicit(self, applyFunc=None): if self.shape == (1, 1): return self if isinstance(self.shape[0], Symbol) or isinstance( self.shape[1], Symbol): loggingCritical( "Error : a Matrix with symbolic number of generations as rows and/or columns cannot be given as an explicit matrix." ) exit() mat = SparseMatrix(*self.shape, {}) for i in range(self.shape[0]): for j in range(self.shape[1]): assump = {} if self.is_realMatrix or (self.is_hermitian and i == j): assump['real'] = True else: assump['complex'] = True if i == j and self.is_antisymmetric: mat[i, j] = 0 elif i <= j: mat[i, j] = Symbol( '{' + str(self) + '}_{' + str(i + 1) + str(j + 1) + '}', **assump) else: if self.is_symmetric: mat[i, j] = mat[j, i] elif self.is_antisymmetric: mat[i, j] = -1 * mat[j, i] elif self.is_hermitian: mat[i, j] = conjugate(mat[j, i]) else: mat[i, j] = Symbol( '{' + str(self) + '}_{' + str(i + 1) + str(j + 1) + '}', **assump) if applyFunc is None: return Matrix(mat) # The following case is needed in Python export retList = mat.tolist() for i, row in enumerate(retList): for j, el in enumerate(row): retList[i][j] = applyFunc(el) return retList
def matrix_coeff(m, s): """returns the matrix valued coefficients N_i in m(x) = N_1 * x**(n-1) + N_2 * x**(n-2) + .. + N_deg(m) Parameters ========== m : Matrix matrix to get coefficient matrices from s : symbol to compute coefficient list (coefficients are ambiguous for expressins with multiple symbols) """ m_deg = matrix_degree(m, s) res = [zeros(m.shape[0], m.shape[1])] * (m_deg + 1) for r, row in enumerate(m.tolist()): for e, entry in enumerate(row): entry_coeff_list = Poly(entry, s).all_coeffs() if simplify(entry) == 0: coeff_deg = 0 else: coeff_deg = degree(entry, s) for c, coeff in enumerate(entry_coeff_list): res[c + m_deg - coeff_deg] += \ SparseMatrix(m.shape[0], m.shape[1], {(r, e): 1}) * coeff return res
def test_as_immutable(): data = [[1, 2], [3, 4]] X = Matrix(data) assert sympify(X) == X.as_immutable() == ImmutableMatrix(data) data = {(0, 0): 1, (0, 1): 2, (1, 0): 3, (1, 1): 4} X = SparseMatrix(2, 2, data) assert sympify(X) == X.as_immutable() == ImmutableSparseMatrix(2, 2, data)
def _csrtodok(csr): """Converts a CSR representation to DOK representation""" smat = {} A, JA, IA, shape = csr for i in range(len(IA) - 1): indices = slice(IA[i], IA[i + 1]) for l, m in zip(A[indices], JA[indices]): smat[i, m] = l return SparseMatrix(*(shape + [smat]))
def forward_stoichiometry(self): """The forward stoichiometric matrix""" matrix = SparseMatrix(len(self._species), len(self._reactions), {}) for col, (back_species, _, _, _) in enumerate(self._reactions.values()): for species, qty in back_species.items(): matrix[(self.species.index(species), col)] = qty return matrix
def test_csrtodok(): h = [[5, 7, 5], [2, 1, 3], [0, 1, 1, 3], [3, 4]] g = [[12, 5, 4], [2, 4, 2], [0, 1, 2, 3], [3, 7]] i = [[1, 3, 12], [0, 2, 4], [0, 2, 3], [2, 5]] j = [[11, 15, 12, 15], [2, 4, 1, 2], [0, 1, 1, 2, 3, 4], [5, 8]] k = [[1, 3], [2, 1], [0, 1, 1, 2], [3, 3]] m = _csrtodok(h) assert isinstance(m, SparseMatrix) assert m == SparseMatrix(3, 4, {(0, 2): 5, (2, 1): 7, (2, 3): 5}) assert _csrtodok(g) == SparseMatrix(3, 7, { (0, 2): 12, (1, 4): 5, (2, 2): 4 }) assert _csrtodok(i) == SparseMatrix([[1, 0, 3, 0, 0], [0, 0, 0, 0, 12]]) assert _csrtodok(j) == SparseMatrix(5, 8, { (0, 2): 11, (2, 4): 15, (3, 1): 12, (4, 2): 15 }) assert _csrtodok(k) == SparseMatrix(3, 3, {(0, 2): 1, (2, 1): 3})
def fillMappingMatrix(self, mappingMatrix, rank, coeffList, newTerm): newMat = SparseMatrix(*mappingMatrix.shape, mappingMatrix._smat) pos = [] for subTerm in newTerm[1].as_coeff_add()[1]: splitTerm = flatten(subTerm.as_coeff_mul()) numeric = [x for x in splitTerm if x.is_number] coeff = str([x for x in splitTerm if not x in numeric][0]) pos.append((coeffList.index(coeff), Mul(*numeric))) for el in pos: newMat[rank, el[0]] = el[1] return newMat
def tomatrix(self): """ Converts MutableDenseNDimArray to Matrix. Can convert only 2-dim array, else will raise error. Examples ======== >>> from sympy.tensor.array import MutableSparseNDimArray >>> a = MutableSparseNDimArray([1 for i in range(9)], (3, 3)) >>> b = a.tomatrix() >>> b Matrix([ [1, 1, 1], [1, 1, 1], [1, 1, 1]]) """ if self.rank() != 2: raise ValueError('Dimensions must be of size of 2') mat_sparse = {} for key, value in self._sparse_array.items(): mat_sparse[self._get_tuple_index(key)] = value return SparseMatrix(self.shape[0], self.shape[1], mat_sparse)
def test_doktocsr(): a = SparseMatrix([[1, 2, 0, 0], [0, 3, 9, 0], [0, 1, 4, 0]]) b = SparseMatrix(4, 6, [ 10, 20, 0, 0, 0, 0, 0, 30, 0, 40, 0, 0, 0, 0, 50, 60, 70, 0, 0, 0, 0, 0, 0, 80 ]) c = SparseMatrix(4, 4, [0, 0, 0, 0, 0, 12, 0, 2, 15, 0, 12, 0, 0, 0, 0, 4]) d = SparseMatrix(10, 10, {(1, 1): 12, (3, 5): 7, (7, 8): 12}) e = SparseMatrix([[0, 0, 0], [1, 0, 2], [3, 0, 0]]) f = SparseMatrix(7, 8, {(2, 3): 5, (4, 5): 12}) assert _doktocsr(a) == [[1, 2, 3, 9, 1, 4], [0, 1, 1, 2, 1, 2], [0, 2, 4, 6], [3, 4]] assert _doktocsr(b) == [[10, 20, 30, 40, 50, 60, 70, 80], [0, 1, 1, 3, 2, 3, 4, 5], [0, 2, 4, 7, 8], [4, 6]] assert _doktocsr(c) == [[12, 2, 15, 12, 4], [1, 3, 0, 2, 3], [0, 0, 2, 4, 5], [4, 4]] assert _doktocsr(d) == [[12, 7, 12], [1, 5, 8], [0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3], [10, 10]] assert _doktocsr(e) == [[1, 2, 3], [0, 2, 0], [0, 0, 2, 3], [3, 3]] assert _doktocsr(f) == [[5, 12], [3, 5], [0, 0, 0, 1, 1, 2, 2, 2], [7, 8]]
def test_sparse_matrix(): return def eye(n): tmp = SparseMatrix(n,n,lambda i,j:0) for i in range(tmp.rows): tmp[i,i] = 1 return tmp def zeros(n): return SparseMatrix(n,n,lambda i,j:0) # test_multiplication a=SparseMatrix(( (1, 2), (3, 1), (0, 6), )) b = SparseMatrix (( (1, 2), (3, 0), )) c= a*b assert c[0,0]==7 assert c[0,1]==2 assert c[1,0]==6 assert c[1,1]==6 assert c[2,0]==18 assert c[2,1]==0 x = Symbol("x") c = b * Symbol("x") assert isinstance(c,SparseMatrix) assert c[0,0] == x assert c[0,1] == 2*x assert c[1,0] == 3*x assert c[1,1] == 0 c = 5 * b assert isinstance(c,SparseMatrix) assert c[0,0] == 5 assert c[0,1] == 2*5 assert c[1,0] == 3*5 assert c[1,1] == 0 #test_power A = SparseMatrix([[2,3],[4,5]]) assert (A**5)[:] == [6140, 8097, 10796, 14237] A = SparseMatrix([[2, 1, 3],[4,2, 4], [6,12, 1]]) assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433] # test_creation x = Symbol("x") a = SparseMatrix([x, 0], [0, 0]) m = a assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x,0,0,0] b = SparseMatrix(2,2, [x, 0, 0, 0]) m = b assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x,0,0,0] assert a == b # test_determinant x, y = Symbol('x'), Symbol('y') assert SparseMatrix([ [1] ]).det() == 1 assert SparseMatrix(( (-3, 2), ( 8, -5) )).det() == -1 assert SparseMatrix(( (x, 1), (y, 2*y) )).det() == 2*x*y-y assert SparseMatrix(( (1, 1, 1), (1, 2, 3), (1, 3, 6) )).det() == 1 assert SparseMatrix(( ( 3, -2, 0, 5), (-2, 1, -2, 2), ( 0, -2, 5, 0), ( 5, 0, 3, 4) )).det() == -289 assert SparseMatrix(( ( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16) )).det() == 0 assert SparseMatrix(( (3, 2, 0, 0, 0), (0, 3, 2, 0, 0), (0, 0, 3, 2, 0), (0, 0, 0, 3, 2), (2, 0, 0, 0, 3) )).det() == 275 assert SparseMatrix(( (1, 0, 1, 2, 12), (2, 0, 1, 1, 4), (2, 1, 1, -1, 3), (3, 2, -1, 1, 8), (1, 1, 1, 0, 6) )).det() == -55 assert SparseMatrix(( (-5, 2, 3, 4, 5), ( 1, -4, 3, 4, 5), ( 1, 2, -3, 4, 5), ( 1, 2, 3, -2, 5), ( 1, 2, 3, 4, -1) )).det() == 11664 assert SparseMatrix(( ( 2, 7, -1, 3, 2), ( 0, 0, 1, 0, 1), (-2, 0, 7, 0, 2), (-3, -2, 4, 5, 3), ( 1, 0, 0, 0, 1) )).det() == 123 # test_submatrix m0 = eye(4) assert m0[0:3, 0:3] == eye(3) assert m0[2:4, 0:2] == zeros(2) m1 = SparseMatrix(3,3, lambda i,j: i+j) assert m1[0,:] == SparseMatrix(1,3,(0,1,2)) assert m1[1:3, 1] == SparseMatrix(2,1,(2,3)) m2 = SparseMatrix([0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]) assert m2[:,-1] == SparseMatrix(4,1,[3,7,11,15]) assert m2[-2:,:] == SparseMatrix([[8,9,10,11],[12,13,14,15]]) # test_submatrix_assignment m = zeros(4) m[2:4, 2:4] = eye(2) assert m == SparseMatrix((0,0,0,0), (0,0,0,0), (0,0,1,0), (0,0,0,1)) m[0:2, 0:2] = eye(2) assert m == eye(4) m[:,0] = SparseMatrix(4,1,(1,2,3,4)) assert m == SparseMatrix((1,0,0,0), (2,1,0,0), (3,0,1,0), (4,0,0,1)) m[:,:] = zeros(4) assert m == zeros(4) m[:,:] = ((1,2,3,4),(5,6,7,8),(9, 10, 11, 12),(13,14,15,16)) assert m == SparseMatrix(((1,2,3,4), (5,6,7,8), (9, 10, 11, 12), (13,14,15,16))) m[0:2, 0] = [0,0] assert m == SparseMatrix(((0,2,3,4), (0,6,7,8), (9, 10, 11, 12), (13,14,15,16))) # test_reshape m0 = eye(3) assert m0.reshape(1,9) == SparseMatrix(1,9,(1,0,0,0,1,0,0,0,1)) m1 = SparseMatrix(3,4, lambda i,j: i+j) assert m1.reshape(4,3) == SparseMatrix((0,1,2), (3,1,2), (3,4,2), (3,4,5)) assert m1.reshape(2,6) == SparseMatrix((0,1,2,3,1,2), (3,4,2,3,4,5)) # test_applyfunc m0 = eye(3) assert m0.applyfunc(lambda x:2*x) == eye(3)*2 assert m0.applyfunc(lambda x: 0 ) == zeros(3) # test_LUdecomp testmat = SparseMatrix([[0,2,5,3], [3,3,7,4], [8,4,0,2], [-2,6,3,4]]) L,U,p = testmat.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-testmat == zeros(4) testmat = SparseMatrix([[6,-2,7,4], [0,3,6,7], [1,-2,7,4], [-9,2,6,3]]) L,U,p = testmat.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-testmat == zeros(4) x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix(((1, x, 1), (2, y, 0), (y, 0, z))) L, U, p = M.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-M == zeros(3) # test_LUsolve A = SparseMatrix([[2,3,5], [3,6,2], [8,3,6]]) x = SparseMatrix(3,1,[3,7,5]) b = A*x soln = A.LUsolve(b) assert soln == x A = SparseMatrix([[0,-1,2], [5,10,7], [8,3,4]]) x = SparseMatrix(3,1,[-1,2,5]) b = A*x soln = A.LUsolve(b) assert soln == x # test_inverse A = eye(4) assert A.inv() == eye(4) assert A.inv("LU") == eye(4) assert A.inv("ADJ") == eye(4) A = SparseMatrix([[2,3,5], [3,6,2], [8,3,6]]) Ainv = A.inv() assert A*Ainv == eye(3) assert A.inv("LU") == Ainv assert A.inv("ADJ") == Ainv # test_cross v1 = Matrix(1,3,[1,2,3]) v2 = Matrix(1,3,[3,4,5]) assert v1.cross(v2) == Matrix(1,3,[-2,4,-2]) assert v1.norm(v1) == 14 # test_cofactor assert eye(3) == eye(3).cofactorMatrix() test = SparseMatrix([[1,3,2],[2,6,3],[2,3,6]]) assert test.cofactorMatrix() == SparseMatrix([[27,-6,-6],[-12,2,3],[-3,1,0]]) test = SparseMatrix([[1,2,3],[4,5,6],[7,8,9]]) assert test.cofactorMatrix() == SparseMatrix([[-3,6,-3],[6,-12,6],[-3,6,-3]]) # test_jacobian x = Symbol('x') y = Symbol('y') L = SparseMatrix(1,2,[x**2*y, 2*y**2 + x*y]) syms = [x,y] assert L.jacobian(syms) == Matrix([[2*x*y, x**2],[y, 4*y+x]]) L = SparseMatrix(1,2,[x, x**2*y**3]) assert L.jacobian(syms) == SparseMatrix([[1, 0], [2*x*y**3, x**2*3*y**2]]) # test_QR A = Matrix([[1,2],[2,3]]) Q, S = A.QRdecomposition() R = Rational assert Q == Matrix([[5**R(-1,2), (R(2)/5)*(R(1)/5)**R(-1,2)], [2*5**R(-1,2), (-R(1)/5)*(R(1)/5)**R(-1,2)]]) assert S == Matrix([[5**R(1,2), 8*5**R(-1,2)], [0, (R(1)/5)**R(1,2)]]) assert Q*S == A assert Q.T * Q == eye(2) # test nullspace # first test reduced row-ech form R = Rational M = Matrix([[5,7,2,1], [1,6,2,-1]]) out, tmp = M.rref() assert out == Matrix([[1,0,-R(2)/23,R(13)/23], [0,1,R(8)/23, R(-6)/23]]) M = Matrix([[1,3,0,2,6,3,1], [-2,-6,0,-2,-8,3,1], [3,9,0,0,6,6,2], [-1,-3,0,1,0,9,3]]) out, tmp = M.rref() assert out == Matrix([[1,3,0,0,2,0,0], [0,0,0,1,2,0,0], [0,0,0,0,0,1,R(1)/3], [0,0,0,0,0,0,0]]) # now check the vectors basis = M.nullspace() assert basis[0] == Matrix([[-3,1,0,0,0,0,0]]) assert basis[1] == Matrix([[0,0,1,0,0,0,0]]) assert basis[2] == Matrix([[-2,0,0,-2,1,0,0]]) assert basis[3] == Matrix([[0,0,0,0,0,R(-1)/3, 1]]) # test eigen x = Symbol('x') y = Symbol('y') eye3 = eye(3) assert eye3.charpoly(x) == (1-x)**3 assert eye3.charpoly(y) == (1-y)**3 # test values M = Matrix([(0,1,-1), (1,1,0), (-1,0,1) ]) vals = M.eigenvals() vals.sort() assert vals == [-1, 1, 2] R = Rational M = Matrix([ [1,0,0], [0,1,0], [0,0,1]]) assert M.eigenvects() == [[1, 3, [Matrix(1,3,[1,0,0]), Matrix(1,3,[0,1,0]), Matrix(1,3,[0,0,1])]]] M = Matrix([ [5,0,2], [3,2,0], [0,0,1]]) assert M.eigenvects() == [[1, 1, [Matrix(1,3,[R(-1)/2,R(3)/2,1])]], [2, 1, [Matrix(1,3,[0,1,0])]], [5, 1, [Matrix(1,3,[1,1,0])]]] assert M.zeros((3, 5)) == SparseMatrix(3, 5, {})
def generate_jacobians(self): """ Generate Jacobians and store to corresponding triplets. The internal indices of equations and variables are stored, alongside the lambda functions. For example, dg/dy is a sparse matrix whose elements are ``(row, col, val)``, where ``row`` and ``col`` are the internal indices, and ``val`` is the numerical lambda function. They will be stored to row -> self.calls._igy col -> self.calls._jgy val -> self.calls._vgy """ logger.debug(f'- Generating Jacobians for {self.class_name}') # clear storage self.df_syms, self.dg_syms = Matrix([]), Matrix([]) self.calls.clear_ijv() # NOTE: SymPy does not allow getting the derivative of an empty array if len(self.g_matrix) > 0: self.dg_syms = self.g_matrix.jacobian(self.vars_list) if len(self.f_matrix) > 0: self.df_syms = self.f_matrix.jacobian(self.vars_list) self.df_sparse = SparseMatrix(self.df_syms) self.dg_sparse = SparseMatrix(self.dg_syms) vars_syms_list = list(self.vars_dict) algebs_and_ext_list = list(self.cache.algebs_and_ext) states_and_ext_list = list(self.cache.states_and_ext) fg_sparse = [self.df_sparse, self.dg_sparse] j_args = defaultdict(list) # argument list for each jacobian call j_calls = defaultdict(list) # jacobian functions (one for each type) for idx, eq_sparse in enumerate(fg_sparse): for item in eq_sparse.row_list(): e_idx, v_idx, e_symbolic = item if idx == 0: eq_name = states_and_ext_list[e_idx] else: eq_name = algebs_and_ext_list[e_idx] var_name = vars_syms_list[v_idx] eqn = self.cache.all_vars[eq_name] # `BaseVar` that corr. to the equation var = self.cache.all_vars[var_name] # `BaseVar` that corr. to the variable jname = f'{eqn.e_code}{var.v_code}' # jac calls with all arguments and stored individually self.calls.append_ijv(jname, e_idx, v_idx, 0) # collect unique arguments for jac calls free_syms = self._check_expr_symbols(e_symbolic) for fs in free_syms: if fs not in j_args[jname]: j_args[jname].append(fs) j_calls[jname].append(e_symbolic) for jname in j_calls: self.calls.j_args[jname] = [str(i) for i in j_args[jname]] self.calls.j[jname] = lambdify(j_args[jname], tuple(j_calls[jname]), modules=self.lambdify_func) self.calls.j_names = list(j_calls.keys()) # The for-loop below is intended to add an epsilon small value to the diagonal of `gy`. # The user should take care of the algebraic equations by using `diag_eps` in `Algeb` definition for var in self.parent.cache.vars_int.values(): if var.diag_eps == 0.0: continue elif var.diag_eps is True: eps = self.parent.system.config.diag_eps else: eps = var.diag_eps if var.e_code == 'g': eq_list = algebs_and_ext_list else: eq_list = states_and_ext_list e_idx = eq_list.index(var.name) v_idx = vars_syms_list.index(var.name) self.calls.append_ijv(f'{var.e_code}{var.v_code}c', e_idx, v_idx, eps)
class SymProcessor: """ A helper class for symbolic processing and code generation. Parameters ---------- parent : Model The `Model` instance to document Attributes ---------- xy : sympy.Matrix variables pretty print in the order of State, ExtState, Algeb, ExtAlgeb f : sympy.Matrix differential equations pretty print g : sympy.Matrix algebraic equations pretty print df : sympy.SparseMatrix df /d (xy) pretty print dg : sympy.SparseMatrix dg /d (xy) pretty print inputs_dict : OrderedDict All possible symbols in equations, including variables, parameters, discrete flags, and config flags. It has the same variables as what ``get_inputs()`` returns. vars_dict : OrderedDict variable-only symbols, which are useful when getting the Jacobian matrices. """ def __init__(self, parent): self.parent = parent # symbols that are input to lambda functions # including parameters, variables, services, configs, and scalars (dae_t, sys_f, sys_mva) self.inputs_dict = OrderedDict() self.lambdify_func = [dict(), 'numpy'] self.vars_dict = OrderedDict() self.vars_int_dict = OrderedDict() # internal variables only self.vars_list = list() self.f_list, self.g_list = list(), list() # symbolic equations in lists self.f_matrix, self.g_matrix, self.s_matrix = list(), list(), list() # equations in matrices # pretty print of variables self.xy = list() # variables in the order of states, algebs self.f, self.g, self.s = list(), list(), list() self.df, self.dg = None, None # get references to the parent attributes self.calls = parent.calls self.cache = parent.cache self.config = parent.config self.class_name = parent.class_name self.tex_names = OrderedDict() def generate_symbols(self): """ Generate symbols for symbolic equation generations. This function should run before other generate equations. Attributes ---------- inputs_dict : OrderedDict name-symbol pair of all parameters, variables and configs vars_dict : OrderedDict name-symbol pair of all variables, in the order of (states_and_ext + algebs_and_ext) """ logger.debug(f'- Generating symbols for {self.class_name}') # clear symbols storage self.f_list, self.g_list = list(), list() self.f_matrix, self.g_matrix = Matrix([]), Matrix([]) # process tex_names defined in model # ----------------------------------------------------------- for key in self.parent.tex_names.keys(): self.tex_names[key] = Symbol(self.parent.tex_names[key]) for instance in self.parent.discrete.values(): for name, tex_name in zip(instance.get_names(), instance.get_tex_names()): self.tex_names[name] = tex_name # ----------------------------------------------------------- for var in self.cache.all_params_names: self.inputs_dict[var] = Symbol(var) for var in self.cache.all_vars_names: tmp = Symbol(var) self.vars_dict[var] = tmp self.inputs_dict[var] = tmp if var in self.cache.vars_int: self.vars_int_dict[var] = tmp # store tex names defined in `self.config` for key in self.config.as_dict(): tmp = Symbol(key) self.inputs_dict[key] = tmp if key in self.config.tex_names: self.tex_names[tmp] = Symbol(self.config.tex_names[key]) # store tex names for pretty printing replacement later for var in self.inputs_dict: if var in self.parent.__dict__ and self.parent.__dict__[var].tex_name is not None: self.tex_names[Symbol(var)] = Symbol(self.parent.__dict__[var].tex_name) self.inputs_dict['dae_t'] = Symbol('dae_t') self.inputs_dict['sys_f'] = Symbol('sys_f') self.inputs_dict['sys_mva'] = Symbol('sys_mva') self.lambdify_func[0]['Indicator'] = lambda x: x self.lambdify_func[0]['imag'] = np.imag self.lambdify_func[0]['real'] = np.real self.lambdify_func[0]['im'] = np.imag self.lambdify_func[0]['re'] = np.real self.vars_list = list(self.vars_dict.values()) # useful for ``.jacobian()`` def _check_expr_symbols(self, expr): """ Check if expression contains unknown symbols. """ fs = expr.free_symbols for item in fs: if item not in self.inputs_dict.values(): raise ValueError(f'{self.class_name} expression "{expr}" contains unknown symbol "{item}"') return fs def generate_equations(self): logger.debug(f'- Generating equations for {self.class_name}') self.f_list, self.g_list = list(), list() self.calls.f = None self.calls.g = None self.calls.f_args = list() self.calls.g_args = list() vars_list = [self.cache.states_and_ext, self.cache.algebs_and_ext] expr_list = [self.f_list, self.g_list] eqn_names = ['f', 'g'] eqn_args = [self.calls.f_args, self.calls.g_args] for vlist, elist, ename, eargs in zip(vars_list, expr_list, eqn_names, eqn_args): sym_args = list() for name, instance in vlist.items(): if instance.e_str is None: elist.append(0) else: try: expr = sympify(instance.e_str, locals=self.inputs_dict) except (SympifyError, TypeError) as e: logger.error('Error parsing equation "%s "for %s.%s', instance.e_str, instance.owner.class_name, name) raise e free_syms = self._check_expr_symbols(expr) for s in free_syms: if s not in sym_args: sym_args.append(s) eargs.append(str(s)) elist.append(expr) if len(elist) == 0 or not any(elist): # `any`, not `all` self.calls.__dict__[ename] = None else: self.calls.__dict__[ename] = lambdify(sym_args, tuple(elist), modules=self.lambdify_func) # convert to SymPy matrices self.f_matrix = Matrix(self.f_list) self.g_matrix = Matrix(self.g_list) def generate_services(self): """ Generate calls for services, including ``ConstService`` and ``VarService`` among others. Sequence is preserved due to possible dependency """ s_args = OrderedDict() s_syms = OrderedDict() s_calls = OrderedDict() for name, instance in self.parent.services.items(): v_str = '0' if instance.v_str is None else instance.v_str try: expr = sympify(v_str, locals=self.inputs_dict) except (SympifyError, TypeError) as e: logger.error(f'Error parsing equation for {instance.owner.class_name}.{name}') raise e self._check_expr_symbols(expr) s_syms[name] = expr s_args[name] = [str(i) for i in expr.free_symbols] s_calls[name] = lambdify(s_args[name], s_syms[name], modules=self.lambdify_func) self.s_matrix = Matrix(list(s_syms.values())) self.calls.s = s_calls self.calls.s_args = s_args def generate_jacobians(self): """ Generate Jacobians and store to corresponding triplets. The internal indices of equations and variables are stored, alongside the lambda functions. For example, dg/dy is a sparse matrix whose elements are ``(row, col, val)``, where ``row`` and ``col`` are the internal indices, and ``val`` is the numerical lambda function. They will be stored to row -> self.calls._igy col -> self.calls._jgy val -> self.calls._vgy """ logger.debug(f'- Generating Jacobians for {self.class_name}') # clear storage self.df_syms, self.dg_syms = Matrix([]), Matrix([]) self.calls.clear_ijv() # NOTE: SymPy does not allow getting the derivative of an empty array if len(self.g_matrix) > 0: self.dg_syms = self.g_matrix.jacobian(self.vars_list) if len(self.f_matrix) > 0: self.df_syms = self.f_matrix.jacobian(self.vars_list) self.df_sparse = SparseMatrix(self.df_syms) self.dg_sparse = SparseMatrix(self.dg_syms) vars_syms_list = list(self.vars_dict) algebs_and_ext_list = list(self.cache.algebs_and_ext) states_and_ext_list = list(self.cache.states_and_ext) fg_sparse = [self.df_sparse, self.dg_sparse] j_args = defaultdict(list) # argument list for each jacobian call j_calls = defaultdict(list) # jacobian functions (one for each type) for idx, eq_sparse in enumerate(fg_sparse): for item in eq_sparse.row_list(): e_idx, v_idx, e_symbolic = item if idx == 0: eq_name = states_and_ext_list[e_idx] else: eq_name = algebs_and_ext_list[e_idx] var_name = vars_syms_list[v_idx] eqn = self.cache.all_vars[eq_name] # `BaseVar` that corr. to the equation var = self.cache.all_vars[var_name] # `BaseVar` that corr. to the variable jname = f'{eqn.e_code}{var.v_code}' # jac calls with all arguments and stored individually self.calls.append_ijv(jname, e_idx, v_idx, 0) # collect unique arguments for jac calls free_syms = self._check_expr_symbols(e_symbolic) for fs in free_syms: if fs not in j_args[jname]: j_args[jname].append(fs) j_calls[jname].append(e_symbolic) for jname in j_calls: self.calls.j_args[jname] = [str(i) for i in j_args[jname]] self.calls.j[jname] = lambdify(j_args[jname], tuple(j_calls[jname]), modules=self.lambdify_func) self.calls.j_names = list(j_calls.keys()) # The for-loop below is intended to add an epsilon small value to the diagonal of `gy`. # The user should take care of the algebraic equations by using `diag_eps` in `Algeb` definition for var in self.parent.cache.vars_int.values(): if var.diag_eps == 0.0: continue elif var.diag_eps is True: eps = self.parent.system.config.diag_eps else: eps = var.diag_eps if var.e_code == 'g': eq_list = algebs_and_ext_list else: eq_list = states_and_ext_list e_idx = eq_list.index(var.name) v_idx = vars_syms_list.index(var.name) self.calls.append_ijv(f'{var.e_code}{var.v_code}c', e_idx, v_idx, eps) def generate_pretty_print(self): """ Generate pretty print variables and equations. """ logger.debug(f"- Generating pretty prints for {self.class_name}") # equation symbols for pretty printing self.f, self.g = Matrix([]), Matrix([]) self.xy = Matrix(list(self.vars_dict.values())).subs(self.tex_names) # get pretty printing equations by substituting symbols self.f = self.f_matrix.subs(self.tex_names) self.g = self.g_matrix.subs(self.tex_names) self.s = self.s_matrix.subs(self.tex_names) # store latex strings nx = len(self.f) ny = len(self.g) self.calls.x_latex = [latex(item) for item in self.xy[:nx]] self.calls.y_latex = [latex(item) for item in self.xy[nx:nx + ny]] self.calls.f_latex = [latex(item) for item in self.f] self.calls.g_latex = [latex(item) for item in self.g] self.calls.s_latex = [latex(item) for item in self.s] self.df = self.df_sparse.subs(self.tex_names) self.dg = self.dg_sparse.subs(self.tex_names) # store init latex strings init_latex = OrderedDict() for name, instance in self.cache.all_vars.items(): if instance.v_str is None and instance.v_iter is None: init_latex[name] = '' else: if instance.v_str is not None: init_latex[name] = latex(self.v_str_syms[name].subs(self.tex_names)) if instance.v_iter is not None: init_latex[name] = latex(self.v_iter_syms[name].subs(self.tex_names)) self.calls.init_latex = init_latex def generate_pycode(self, pycode_path): """ Create output source code file for generated code. Generated code are stored at ``~/.andes/pycode``. """ import pprint pycode_path = get_pycode_path(pycode_path, mkdir=True) file_path = os.path.join(pycode_path, f'{self.class_name}.py') header = \ """from collections import OrderedDict # NOQA from numpy import nan, pi, sin, cos, tan, sqrt, exp, select # NOQA from numpy import greater_equal, less_equal, greater, less # NOQA from numpy import logical_and, logical_or, logical_not # NOQA from numpy import array, real, imag, conj, angle, arctan, radians # NOQA from numpy import log # NOQA """ with open(file_path, 'w') as f: f.write(header) f.write(self._rename_func(self.calls.f, 'f_update')) f.write(self._rename_func(self.calls.g, 'g_update')) for name in self.calls.j: f.write(self._rename_func(self.calls.j[name], f'{name}_update')) # initialization: assignments for name in self.calls.ia: f.write(self._rename_func(self.calls.ia[name], f'{name}_ia')) for name in self.calls.ii: f.write(self._rename_func(self.calls.ii[name], f'{name}_ii')) for name in self.calls.ij: f.write(self._rename_func(self.calls.ij[name], f'{name}_ij')) # services for name in self.calls.s: f.write(self._rename_func(self.calls.s[name], f'{name}_svc')) # variables for name in dilled_vars: f.write(f'\n{name} = ' + pprint.pformat(self.calls.__dict__[name])) def _rename_func(self, func, func_name): """ Rename the function name and return source code. This function does not check for name conflicts. Install `yapf` for optional code reformatting (takes extra processing time). """ import inspect if func is None: return f"# empty {func_name}\n" src = inspect.getsource(func) src = src.replace("def _lambdifygenerated(", f"def {func_name}(") # remove `Indicator` src = src.replace("Indicator", "") if self.parent.system.config.yapf_pycode: try: from yapf.yapflib.yapf_api import FormatCode src = FormatCode(src, style_config='pep8')[0] # drop the encoding `None` except ImportError: logger.warning("`yapf` not installed. Skipped code reformatting.") src += '\n' return src def generate_dependency(self): """ Generate dependency list and initialization order. """ self.v_str_syms = OrderedDict() self.v_iter_syms = OrderedDict() deps = dict() # convert to symbols for name, instance in self.cache.all_vars.items(): if instance.v_str is not None: sympified = sympify(instance.v_str, locals=self.inputs_dict) self._check_expr_symbols(sympified) self.v_str_syms[name] = sympified else: # default initial values to zero sympified = sympify('0.0', locals=self.inputs_dict) self.v_str_syms[name] = sympified if instance.v_iter is not None: sympified = sympify(instance.v_iter, locals=self.inputs_dict) self._check_expr_symbols(sympified) self.v_iter_syms[name] = sympified # store deps for explicit and iterative initializers for name, expr in self.v_str_syms.items(): _store_deps(name, expr, self.vars_dict, deps) for name, expr in self.v_iter_syms.items(): _store_deps(name, expr, self.vars_dict, deps) # resolve dependency self.init_seq = resolve_deps(deps) self.calls.init_seq = self.init_seq def check_v_iter(self): """ Helper function to check if `v_iter` is defined for variables with circular dependencies. """ for item in self.init_seq: if not isinstance(item, list): continue for vi in item: if self.cache.all_vars[vi].v_iter is None: logger.error("%s: v_iter not defined for %s" % (self.class_name, vi)) def generate_init(self): """ Generate initialization equations. """ self.generate_dependency() self.check_v_iter() self.generate_init_eqn() self.lambdify_init() def generate_init_eqn(self): """ Generate initialization equations. The RHS of assignment equations ``v = v_str(x, y)`` or RHS of iterative initialization equations in the form of ``0 = v_iter(x, y)`` will be stored to ``self.init_list``. A list of flags will be stored to ``self.init_flag`` with 0 for assignments and 1 for iterative. For iteratively initialized variables that require assigned initial values (to improve convergence), the initial value can be provided through `self.v_str`. """ self.init_asn = OrderedDict() # assignment-type initialization self.init_itn = OrderedDict() # iterative initialization self.init_itn_vars = OrderedDict() # variables corr. to iterative vars self.init_jac = OrderedDict() for item in self.init_seq: if isinstance(item, str): instance = self.parent.__dict__[item] if instance.v_str is not None: self.init_asn[item] = self.v_str_syms[item] if instance.v_iter is not None: self.init_itn[item] = self.v_iter_syms[item] elif isinstance(item, list): name_concat = '_'.join(item) eqn_set = Matrix([self.v_iter_syms[name] for name in item]) self.init_itn[name_concat] = eqn_set self.init_itn_vars[name_concat] = item for vv in item: instance = self.parent.__dict__[vv] if instance.v_str is not None: self.init_asn[vv] = self.v_str_syms[vv] for name, expr in self.init_itn.items(): vars_iter = OrderedDict() for item in self.init_itn_vars[name]: vars_iter[item] = self.vars_dict[item] self.init_jac[name] = expr.jacobian(list(vars_iter.values())) def lambdify_init(self): """ Convert equations and Jacobians to lambda functions. """ init_a = OrderedDict() init_i = OrderedDict() init_j = OrderedDict() ia_args = OrderedDict() # arguments for assignment init. ii_args = OrderedDict() # arguments for iterative init. ij_args = OrderedDict() for name, expr in self.init_asn.items(): self._check_expr_symbols(expr) ia_args[name] = [str(i) for i in expr.free_symbols] init_a[name] = lambdify(ia_args[name], expr, modules=self.lambdify_func) for name, expr in self.init_itn.items(): self._check_expr_symbols(expr) ii_args[name] = [str(i) for i in expr.free_symbols] init_i[name] = lambdify(ii_args[name], expr, modules=self.lambdify_func) jexpr = self.init_jac[name] ij_args[name] = [str(i) for i in jexpr.free_symbols] init_j[name] = lambdify(ij_args[name], jexpr, modules=self.lambdify_func) self.calls.ia = init_a self.calls.ii = init_i self.calls.ij = init_j self.calls.ia_args = ia_args self.calls.ii_args = ii_args self.calls.ij_args = ij_args
def C2Fmat(self): return SparseMatrix(self.nF, self.nF, {k: v for k, v in self.C2F.dic.items()})
def eye(n): tmp = SparseMatrix(n,n,lambda i,j:0) for i in range(tmp.rows): tmp[i,i] = 1 return tmp
def banded(*args, **kwargs): """Returns a SparseMatrix from the given dictionary describing the diagonals of the matrix. The keys are positive for upper diagonals and negative for those below the main diagonal. The values may be: * expressions or single-argument functions, * lists or tuples of values, * matrices Unless dimensions are given, the size of the returned matrix will be large enough to contain the largest non-zero value provided. kwargs ====== rows : rows of the resulting matrix; computed if not given. cols : columns of the resulting matrix; computed if not given. Examples ======== >>> from sympy import banded, ones, Matrix >>> from sympy.abc import x If explicit values are given in tuples, the matrix will autosize to contain all values, otherwise a single value is filled onto the entire diagonal: >>> banded({1: (1, 2, 3), -1: (4, 5, 6), 0: x}) Matrix([ [x, 1, 0, 0], [4, x, 2, 0], [0, 5, x, 3], [0, 0, 6, x]]) A function accepting a single argument can be used to fill the diagonal as a function of diagonal index (which starts at 0). The size (or shape) of the matrix must be given to obtain more than a 1x1 matrix: >>> s = lambda d: (1 + d)**2 >>> banded(5, {0: s, 2: s, -2: 2}) Matrix([ [1, 0, 1, 0, 0], [0, 4, 0, 4, 0], [2, 0, 9, 0, 9], [0, 2, 0, 16, 0], [0, 0, 2, 0, 25]]) The diagonal of matrices placed on a diagonal will coincide with the indicated diagonal: >>> vert = Matrix([1, 2, 3]) >>> banded({0: vert}, cols=3) Matrix([ [1, 0, 0], [2, 1, 0], [3, 2, 1], [0, 3, 2], [0, 0, 3]]) >>> banded(4, {0: ones(2)}) Matrix([ [1, 1, 0, 0], [1, 1, 0, 0], [0, 0, 1, 1], [0, 0, 1, 1]]) Errors are raised if the designated size will not hold all values an integral number of times. Here, the rows are designated as odd (but an even number is required to hold the off-diagonal 2x2 ones): >>> banded({0: 2, 1: ones(2)}, rows=5) Traceback (most recent call last): ... ValueError: sequence does not fit an integral number of times in the matrix And here, an even number of rows is given...but the square matrix has an even number of columns, too. As we saw in the previous example, an odd number is required: >>> banded(4, {0: 2, 1: ones(2)}) # trying to make 4x4 and cols must be odd Traceback (most recent call last): ... ValueError: sequence does not fit an integral number of times in the matrix A way around having to count rows is to enclosing matrix elements in a tuple and indicate the desired number of them to the right: >>> banded({0: 2, 2: (ones(2),)*3}) Matrix([ [2, 0, 1, 1, 0, 0, 0, 0], [0, 2, 1, 1, 0, 0, 0, 0], [0, 0, 2, 0, 1, 1, 0, 0], [0, 0, 0, 2, 1, 1, 0, 0], [0, 0, 0, 0, 2, 0, 1, 1], [0, 0, 0, 0, 0, 2, 1, 1]]) An error will be raised if more than one value is written to a given entry. Here, the ones overlap with the main diagonal if they are placed on the first diagonal: >>> banded({0: (2,)*5, 1: (ones(2),)*3}) Traceback (most recent call last): ... ValueError: collision at (1, 1) By placing a 0 at the bottom left of the 2x2 matrix of ones, the collision is avoided: >>> u2 = Matrix([ ... [1, 1], ... [0, 1]]) >>> banded({0: [2]*5, 1: [u2]*3}) Matrix([ [2, 1, 1, 0, 0, 0, 0], [0, 2, 1, 0, 0, 0, 0], [0, 0, 2, 1, 1, 0, 0], [0, 0, 0, 2, 1, 0, 0], [0, 0, 0, 0, 2, 1, 1], [0, 0, 0, 0, 0, 0, 1]]) """ from sympy import Dict, Dummy, SparseMatrix try: if len(args) not in (1, 2, 3): raise TypeError if not isinstance(args[-1], (dict, Dict)): raise TypeError if len(args) == 1: rows = kwargs.get('rows', None) cols = kwargs.get('cols', None) if rows is not None: rows = as_int(rows) if cols is not None: cols = as_int(cols) elif len(args) == 2: rows = cols = as_int(args[0]) else: rows, cols = map(as_int, args[:2]) # fails with ValueError if any keys are not ints _ = all(as_int(k) for k in args[-1]) except (ValueError, TypeError): raise TypeError( filldedent('''unrecognized input to banded: expecting [[row,] col,] {int: value}''')) def rc(d): # return row,col coord of diagonal start r = -d if d < 0 else 0 c = 0 if r else d return r, c smat = {} undone = [] tba = Dummy() # first handle objects with size for d, v in args[-1].items(): r, c = rc(d) # note: only list and tuple are recognized since this # will allow other Basic objects like Tuple # into the matrix if so desired if isinstance(v, (list, tuple)): extra = 0 for i, vi in enumerate(v): i += extra if is_sequence(vi): vi = SparseMatrix(vi) smat[r + i, c + i] = vi extra += min(vi.shape) - 1 else: smat[r + i, c + i] = vi elif is_sequence(v): v = SparseMatrix(v) rv, cv = v.shape if rows and cols: nr, xr = divmod(rows - r, rv) nc, xc = divmod(cols - c, cv) x = xr or xc do = min(nr, nc) elif rows: do, x = divmod(rows - r, rv) elif cols: do, x = divmod(cols - c, cv) else: do = 1 x = 0 if x: raise ValueError( filldedent(''' sequence does not fit an integral number of times in the matrix''')) j = min(v.shape) for i in range(do): smat[r, c] = v r += j c += j elif v: smat[r, c] = tba undone.append((d, v)) s = SparseMatrix(None, smat) # to expand matrices smat = s._smat # check for dim errors here if rows is not None and rows < s.rows: raise ValueError('Designated rows %s < needed %s' % (rows, s.rows)) if cols is not None and cols < s.cols: raise ValueError('Designated cols %s < needed %s' % (cols, s.cols)) if rows is cols is None: rows = s.rows cols = s.cols elif rows is not None and cols is None: cols = max(rows, s.cols) elif cols is not None and rows is None: rows = max(cols, s.rows) def update(i, j, v): # update smat and make sure there are # no collisions if v: if (i, j) in smat and smat[i, j] not in (tba, v): raise ValueError('collision at %s' % ((i, j), )) smat[i, j] = v if undone: for d, vi in undone: r, c = rc(d) v = vi if callable(vi) else lambda _: vi i = 0 while r + i < rows and c + i < cols: update(r + i, c + i, v(i)) i += 1 return SparseMatrix(rows, cols, smat)
def ytmat(self, a): return SparseMatrix( self.nF, self.nF, {k[1:]: v for k, v in self.yt.dic.items() if k[0] == a})
def Gmat(self): return SparseMatrix( self.nGi, self.nGi, {(self.gi.index(i), self.gi.index(j)): self.G_(i, j) for i in self.gi for j in self.gi})
def test_as_immutable(): X = Matrix([[1, 2], [3, 4]]) assert sympify(X) == X.as_immutable() == ImmutableMatrix([[1, 2], [3, 4]]) X = SparseMatrix(5, 5, {}) assert sympify(X) == X.as_immutable() == ImmutableSparseMatrix( [[0 for i in range(5)] for i in range(5)])
def constructMapping(self, RGmodule): loggingInfo("Mapping the model onto the general Lagrangian ...") #Gauge couplings mapping, taking into account possible kinetic mixing noMix = {} mix = {} alreadyTaken = set() for el in itertools.combinations_with_replacement( range(RGmodule.nGi), 2): A, B = [RGmodule.gi[i] for i in el] c = RGmodule.G_(A, B) if c != 0 and c not in alreadyTaken: dic = noMix if A == B else mix if not self.upper: A, B = B, A dic[(A, B)] = len(dic) alreadyTaken.add(c) newInds = {**noMix, **{k: v + len(noMix) for k, v in mix.items()}} gaugeMatrix = zeros(len(newInds)) def delta(A, B): if A == B: return 1 return 0 def G(A, B): if RGmodule.G_(A, B) == 0: return 0 if not self.kinMix or A not in RGmodule.Ugauge or B not in RGmodule.Ugauge: return sqrt(RGmodule.G_(A, B)).args[0] i, j = RGmodule.Ugauge.index(A), RGmodule.Ugauge.index(B) return self.kinMat[i, j] for (A, B), X in newInds.items(): for (C, D), Y in newInds.items(): gaugeMatrix[X, Y] = G(B, D) * delta(A, C) + G(A, D) * delta(B, C) gaugeMatrix = simplify(gaugeMatrix.inv()) couplingType = 'GaugeCouplings' self.potential[couplingType] = {} for c in self.gaugeCouplings: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = gaugeMatrix * self.betaFactor self.toCalculate[couplingType] = list(newInds.keys()) count = 0 translation = self.translateDic(RGmodule) for couplingType in self.potential: if couplingType == 'Definitions': continue if (couplingType in translation and self.potential[couplingType] != {} and translation[couplingType] != {}): coeffList = [] dicList = [] mappingMatrix = [] sortedList = [] coeffList = [c for c in self.potential[couplingType].keys()] mappingMatrix = SparseMatrix(len(coeffList), len(coeffList), 0) sortedList = sorted( [(key, val) for key, val in translation[couplingType].items() if not (type(key[-1]) == bool and key[-1] == True)], key=lambda x: (len(set(x[0])), len(x[1].as_coeff_add()[1]), x[0])) trys = 0 for el in sortedList: trys += 1 matTry = self.fillMappingMatrix(mappingMatrix, coeffList, el) if (matTry.rank() > mappingMatrix.rank()): mappingMatrix = matTry dicList.append(el[0]) count += 1 print_progress(count, self.nCouplings, prefix=' ' * 4, bar_length=20, printTime=self.times, logProgress=True) if matTry.rank() == len(coeffList): break try: self.lagrangianMapping[couplingType] = Matrix( mappingMatrix).inv() * self.betaFactor except: # from sympy import pretty loggingCritical( "\nError in Lagrangian mapping : matrix of couplings is not invertible." ) loggingCritical("\tCoupling type : " + couplingType) # loggingCritical("\t\t" + pretty(mappingMatrix).replace("\n", "\n\t\t")) exit() self.toCalculate[couplingType] = dicList # Add vevs and anomalous dimensions by hand (not related to the Lagrangian) if self.vevs != {}: couplingType = 'Vevs' self.potential[couplingType] = {} for c in self.vevs: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye(len( self.vevs)) * self.betaFactor self.toCalculate[couplingType] = list(RGmodule.Vdic.keys()) if self.fermionAnomalous != {}: couplingType = 'FermionAnomalous' self.potential[couplingType] = {} for c in self.fermionAnomalous: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye( len(self.fermionAnomalous)) self.toCalculate[couplingType] = list(RGmodule.gammaFdic.keys()) if self.scalarAnomalous != {}: couplingType = 'ScalarAnomalous' self.potential[couplingType] = {} for c in self.scalarAnomalous: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye( len(self.scalarAnomalous)) self.toCalculate[couplingType] = list(RGmodule.gammaSdic.keys())
def constructMapping(self, RGmodule): loggingInfo("Mapping the model onto the general Lagrangian ...") #Gauge couplings mapping, taking into account possible kinetic mixing noMix = {} mix = {} alreadyTaken = set() for el in itertools.combinations_with_replacement( range(RGmodule.nGi), 2): A, B = [RGmodule.gi[i] for i in el] c = RGmodule.G_(A, B) if c != 0 and c not in alreadyTaken: dic = noMix if A == B else mix if not self.upper: A, B = B, A dic[(A, B)] = len(dic) alreadyTaken.add(c) newInds = {**noMix, **{k: v + len(noMix) for k, v in mix.items()}} gaugeMatrix = zeros(len(newInds)) def delta(A, B): if A == B: return 1 return 0 def G(A, B): if RGmodule.G_(A, B) == 0: return 0 if not self.kinMix or A not in RGmodule.Ugauge or B not in RGmodule.Ugauge: return sqrt(RGmodule.G_(A, B)).args[0] i, j = RGmodule.Ugauge.index(A), RGmodule.Ugauge.index(B) return self.kinMat[i, j] for (A, B), X in newInds.items(): for (C, D), Y in newInds.items(): gaugeMatrix[X, Y] = G(B, D) * delta(A, C) + G(A, D) * delta(B, C) gaugeMatrix = simplify(gaugeMatrix.inv()) couplingType = 'GaugeCouplings' self.potential[couplingType] = {} for c in self.gaugeCouplings: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = gaugeMatrix * self.betaFactor self.toCalculate[couplingType] = list(newInds.keys()) count = 0 translation = self.translateDic(RGmodule) for couplingType in self.potential: if couplingType == 'Definitions': continue if (couplingType in translation and self.potential[couplingType] != {} and translation[couplingType] != {}): coeffList = [c for c in self.potential[couplingType].keys()] mappingMatrix = SparseMatrix(len(coeffList), len(coeffList), 0) dicList = [] auxDic = {} sortFunc = lambda x: (len(set(x[0])), len(x[1].as_coeff_add()[1]), x[0]) for key, val in translation[couplingType].items(): if key[-1] is True: continue if val not in auxDic: auxDic[val] = key elif sortFunc((key, val)) < sortFunc((auxDic[val], val)): auxDic[val] = key rank = 0 for v, k in auxDic.items(): matTry = self.fillMappingMatrix(mappingMatrix, rank, coeffList, (k, v)) newRank = matTry.rank() if (newRank > rank): mappingMatrix = matTry rank = newRank dicList.append(k) count += 1 print_progress(count, self.nCouplings, prefix=' ' * 4, bar_length=20, printTime=self.times, logProgress=True) if newRank == len(coeffList): self.lagrangianMapping[couplingType] = Matrix( mappingMatrix).inv() * self.betaFactor break else: # The mapping matrix is not invertible ns = mappingMatrix.nullspace() cVec = Matrix([ Symbol('O(' + el + ')') for el in coeffList ]).transpose() errorMess = "\n\nError in Lagrangian mapping: matrix of couplings is not invertible. " errorMess += f"The following operators in '{couplingType}' are linearly dependent:\n\n" for vec in ns: errorMess += f" {(cVec*vec)[0,0]} = 0\n" loggingCritical(errorMess[:-1]) exit() self.toCalculate[couplingType] = dicList # Add vevs and anomalous dimensions by hand (not related to the Lagrangian) if self.vevs != {}: couplingType = 'Vevs' self.potential[couplingType] = {} for c in self.vevs: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye(len( self.vevs)) * self.betaFactor self.toCalculate[couplingType] = list(RGmodule.Vdic.keys()) if self.fermionAnomalous != {}: couplingType = 'FermionAnomalous' self.potential[couplingType] = {} for c in self.fermionAnomalous: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye( len(self.fermionAnomalous)) self.toCalculate[couplingType] = list(RGmodule.gammaFdic.keys()) if self.scalarAnomalous != {}: couplingType = 'ScalarAnomalous' self.potential[couplingType] = {} for c in self.scalarAnomalous: self.potential[couplingType][c] = 0 self.lagrangianMapping[couplingType] = eye( len(self.scalarAnomalous)) self.toCalculate[couplingType] = list(RGmodule.gammaSdic.keys())
def test_SparseMatrix(): M = SparseMatrix([[x**+1, 1], [y, x + y]]) assert str(M) == sstr(M) == "[x, 1]\n[y, x + y]"
def test_im(): x, y = symbols('x,y') a, b = symbols('a,b', real=True) r = Symbol('r', real=True) i = Symbol('i', imaginary=True) assert im(nan) == nan assert im(oo * I) == oo assert im(-oo * I) == -oo assert im(0) == 0 assert im(1) == 0 assert im(-1) == 0 assert im(E * I) == E assert im(-E * I) == -E assert unchanged(im, x) assert im(x * I) == re(x) assert im(r * I) == r assert im(r) == 0 assert im(i * I) == 0 assert im(i) == -I * i assert im(x + y) == im(x + y) assert im(x + r) == im(x) assert im(x + r * I) == im(x) + r assert im(im(x) * I) == im(x) assert im(2 + I) == 1 assert im(x + I) == im(x) + 1 assert im(x + y * I) == im(x) + re(y) assert im(x + r * I) == im(x) + r assert im(log(2 * I)) == pi / 2 assert im((2 + I)**2).expand(complex=True) == 4 assert im(conjugate(x)) == -im(x) assert conjugate(im(x)) == im(x) assert im(x).as_real_imag() == (im(x), 0) assert im(i * r * x).diff(r) == im(i * x) assert im(i * r * x).diff(i) == -I * re(r * x) assert im( sqrt(a + b * I)) == (a**2 + b**2)**Rational(1, 4) * sin(atan2(b, a) / 2) assert im(a * (2 + b * I)) == a * b assert im((1 + sqrt(a + b*I))/2) == \ (a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2)/2 assert im(x).rewrite(re) == -S.ImaginaryUnit * (x - re(x)) assert (x + im(y)).rewrite(im, re) == x - S.ImaginaryUnit * (y - re(y)) a = Symbol('a', algebraic=True) t = Symbol('t', transcendental=True) x = Symbol('x') assert re(a).is_algebraic assert re(x).is_algebraic is None assert re(t).is_algebraic is False assert im(S.ComplexInfinity) == S.NaN n, m, l = symbols('n m l') A = MatrixSymbol('A', n, m) assert im(A) == (S(1) / (2 * I)) * (A - conjugate(A)) A = Matrix([[1 + 4 * I, 2], [0, -3 * I]]) assert im(A) == Matrix([[4, 0], [0, -3]]) A = ImmutableMatrix([[1 + 3 * I, 3 - 2 * I], [0, 2 * I]]) assert im(A) == ImmutableMatrix([[3, -2], [0, 2]]) X = ImmutableSparseMatrix([[i * I + i for i in range(5)] for i in range(5)]) Y = SparseMatrix([[i for i in range(5)] for i in range(5)]) assert im(X).as_immutable() == Y X = FunctionMatrix(3, 3, Lambda((n, m), n + m * I)) assert im(X) == Matrix([[0, 1, 2], [0, 1, 2], [0, 1, 2]])
def zeros(n): return SparseMatrix(n,n,lambda i,j:0)
def matrices(self): ''' Returns the representation matrices in an orthonormal basis. The return value is a dictionary. The key represents the generators and the value is a matrix. The key may be an integer or a tuple. If it is an integer i, it denotes the ith Cartan generator. If it is a tuple, it denotes the root given by that tuple (in the q-p notation). ''' cartan_matrix = self.lie_group.cartan_matrix() states = self.states() representation_dimension = self.dimension() representation_matrices = {} # calculate representation matrices for simple roots for i in range(self.lie_group.dimension): simple_root_i_nonzero = {} for level in states: for weight in states[level]: states_for_this_weight = states[level][weight]["states"] for state in states_for_this_weight: state_index1 = state["index"] matrix_element_information = state[ "matrix_element_information"] for entry in matrix_element_information: if entry["direction"] == i: parent_state = states[entry["level"]][ entry["weight"]]["states"][ entry["state_num"]] state_index2 = parent_state["index"] simple_root_i_nonzero[( state_index2, state_index1)] = ( entry["matrix_element"]) simple_root_i_matrix = SparseMatrix(representation_dimension, representation_dimension, simple_root_i_nonzero) representation_matrices[tuple( self.lie_group.simple_root_pq(i, cartan_matrix))] = ( simple_root_i_matrix) # convert to orthonormal basis rotation_to_ob_nonzero = {} for level in states: for weight in states[level]: rotation_matrix = states[level][weight]["rotation_to_ob"] start_index = states[level][weight]["start_index"] end_index = states[level][weight]["end_index"] rotation_matrix_nonzero = dict( [((i,j),rotation_matrix[i-start_index, j-start_index]) for i in range(start_index, end_index+1) for j in range(start_index, end_index+1)] ) rotation_to_ob_nonzero.update(rotation_matrix_nonzero) rotation_to_ob = SparseMatrix(representation_dimension, representation_dimension, rotation_to_ob_nonzero) for key in representation_matrices: rotated_matrix = rotation_to_ob.multiply( representation_matrices[key].multiply(rotation_to_ob.T)) rotated_matrix.simplify() representation_matrices[key] = rotated_matrix # calculate representation matrices for cartan generators fundamental_weights = self.lie_group.fundamental_weights() cartan_matrices_nonzero = [{} for i in range( self.lie_group.dimension)] for level in states: for weight in states[level]: weight_vector = [sum([weight[i]*fundamental_weights[i][j] for i in range(self.lie_group.dimension)]) for j in range(self.lie_group.dimension)] states_for_this_weight = states[level][weight]["states"] for state in states_for_this_weight: state_index = state["index"] for i in range(self.lie_group.dimension): cartan_matrices_nonzero[i][(state_index, state_index)] = weight_vector[i] for i in range(self.lie_group.dimension): representation_matrices[i] = SparseMatrix(representation_dimension, representation_dimension, cartan_matrices_nonzero[i]) # calculate representation matrices for other positive roots positive_roots = self.lie_group.positive_roots()[0] positive_roots_list = [(key,) + positive_roots[key] for key in positive_roots if isinstance(positive_roots[key][1],list)] positive_roots_list = sorted(positive_roots_list, key = lambda item : item[3]) for i in range(len(positive_roots_list)): matrix1 = representation_matrices[positive_roots_list[i][2][0]] matrix2 = representation_matrices[positive_roots_list[i][2][1]] positive_root_matrix = matrix1.multiply(matrix2).add( - matrix2.multiply(matrix1)) factor = positive_roots_list[i][1] root = positive_roots_list[i][0] # sympy cannot multiply a Pow object and a SparseMatrix object # to produce a SparseMatrix object. It produces a Mul object # instead. That's why factor*positive_root_matrix doesn't work. representation_matrices[root] = positive_root_matrix.applyfunc( lambda i : factor*i) # generate matrices for negative roots keys = list(representation_matrices.keys()) for key in keys: if isinstance(key,tuple): new_key = tuple([-i for i in key]) representation_matrices[new_key] = ( representation_matrices[key].T) return representation_matrices
def __new__(self, *args, **kwargs): """ sMat(i,j) returns an empty i x j matrix (i.e. SparseMatrix(i,j,{})""" if len(args) == 2: return SparseMatrix.__new__(self, *args, {}) return SparseMatrix.__new__(self, *args, **kwargs)
def S2Smat(self): return SparseMatrix( self.nGi, self.nGi, {(self.gi.index(k[0]), self.gi.index(k[1])): v for k, v in self.S2S.dic.items()})
def Y2Ftmat(self): return SparseMatrix(self.nF, self.nF, {k: v for k, v in self.Y2Ft.dic.items()})
CD = Matrix( S('''[ [ -3/4, 45/32 - 37*I/16, 0, 0], [-149/64 + 49*I/32, -177/128 - 1369*I/128, 0, -2063/256 + 541*I/128], [ 0, 9/4 + 55*I/16, 2473/256 + 137*I/64, 0], [ 0, 0, 0, -177/128 - 1369*I/128]]''' )) DD = Matrix( S('''[ [ -3/4, 45/32 - 37*I/16, 0, 0], [-149/64 + 49*z/32, -177/128 - 1369*I/128, 0, 541*y/128 - 2063/256], [ 0, 9/4 + 55*I/16, 137*z/64 + 2473/256, 0], [ 0, 0, 0, -1369*x/128 - 177/128]]''' )) ADS = SparseMatrix(AD) BDS = SparseMatrix(BD) CDS = SparseMatrix(CD) DDS = SparseMatrix(DD) O4 = Matrix(4, 1, [1, 1, 1, 1]) class TimePow4: def time_A(self): A**4 def time_B(self): B**4 def time_C(self):
def test_SparseMatrix(): M = SparseMatrix([[x**+1, 1], [y, x + y]]) assert str(M) == "Matrix([[x, 1], [y, x + y]])" assert sstr(M) == "Matrix([\n[x, 1],\n[y, x + y]])"
def Y2Smat(self): return SparseMatrix(self.nS, self.nS, {k: v for k, v in self.Y2S.dic.items()})
def test_re(): x, y = symbols('x,y') a, b = symbols('a,b', real=True) r = Symbol('r', real=True) i = Symbol('i', imaginary=True) assert re(nan) == nan assert re(oo) == oo assert re(-oo) == -oo assert re(0) == 0 assert re(1) == 1 assert re(-1) == -1 assert re(E) == E assert re(-E) == -E assert unchanged(re, x) assert re(x * I) == -im(x) assert re(r * I) == 0 assert re(r) == r assert re(i * I) == I * i assert re(i) == 0 assert re(x + y) == re(x + y) assert re(x + r) == re(x) + r assert re(re(x)) == re(x) assert re(2 + I) == 2 assert re(x + I) == re(x) assert re(x + y * I) == re(x) - im(y) assert re(x + r * I) == re(x) assert re(log(2 * I)) == log(2) assert re((2 + I)**2).expand(complex=True) == 3 assert re(conjugate(x)) == re(x) assert conjugate(re(x)) == re(x) assert re(x).as_real_imag() == (re(x), 0) assert re(i * r * x).diff(r) == re(i * x) assert re(i * r * x).diff(i) == I * r * im(x) assert re( sqrt(a + b * I)) == (a**2 + b**2)**Rational(1, 4) * cos(atan2(b, a) / 2) assert re(a * (2 + b * I)) == 2 * a assert re((1 + sqrt(a + b*I))/2) == \ (a**2 + b**2)**Rational(1, 4)*cos(atan2(b, a)/2)/2 + Rational(1, 2) assert re(x).rewrite(im) == x - S.ImaginaryUnit * im(x) assert (x + re(y)).rewrite(re, im) == x + y - S.ImaginaryUnit * im(y) a = Symbol('a', algebraic=True) t = Symbol('t', transcendental=True) x = Symbol('x') assert re(a).is_algebraic assert re(x).is_algebraic is None assert re(t).is_algebraic is False assert re(S.ComplexInfinity) == S.NaN n, m, l = symbols('n m l') A = MatrixSymbol('A', n, m) assert re(A) == (S(1) / 2) * (A + conjugate(A)) A = Matrix([[1 + 4 * I, 2], [0, -3 * I]]) assert re(A) == Matrix([[1, 2], [0, 0]]) A = ImmutableMatrix([[1 + 3 * I, 3 - 2 * I], [0, 2 * I]]) assert re(A) == ImmutableMatrix([[1, 3], [0, 0]]) X = SparseMatrix([[2 * j + i * I for i in range(5)] for j in range(5)]) assert re(X) - Matrix([[0, 0, 0, 0, 0], [2, 2, 2, 2, 2], [4, 4, 4, 4, 4], [6, 6, 6, 6, 6], [8, 8, 8, 8, 8] ]) == Matrix.zeros(5) assert im(X) - Matrix([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4] ]) == Matrix.zeros(5) X = FunctionMatrix(3, 3, Lambda((n, m), n + m * I)) assert re(X) == Matrix([[0, 0, 0], [1, 1, 1], [2, 2, 2]])
def Tsmat(self, A): return SparseMatrix( self.nS, self.nS, {k[1:]: v for k, v in self.Ts.dic.items() if k[0] == A})