def SpreSmSp(op): opN = op.shape[0] opSM = sm.SparseMatrix(op) #This is essentially just a sparse block-matrix SpreS = sm.SparseMatrix(opN**2, opN**2, {(opN * k, opN * k): opSM for k in range(opN)}) return SpreS
def matrices(self): """ Let M be the number of possible states. Returns N x M State matrix and M x M transition matrix Outputs: S: N x M state matrix T: M x M transition matrix V: M x M transition matrix weighted by transition "speeds" (+1 move right, -1 move left) """ states = self.enum_states() M = len(states) transitions_speeds = [(s, self.transitions(State(s)), self.speeds(State(s))) for s in states] S = np.zeros((self.N, M)) if self.is_symbolic() == True: T = sympy.SparseMatrix(M, M, 0) V = sympy.SparseMatrix(M, M, 0) else: T = sparse.csr_matrix((M, M)) V = sparse.csr_matrix((M, M)) k = 0 for s, t, v in transitions_speeds: i = self.state_index(s) S[:, i] = State(s).values for r in t: j = self.state_index(r) T[j, i] = t[r] if r in v: V[j, i] = v[r] k += 1 V = T.multiply(V) # speed weighted by probabilities return S, T, V
def SpostSmSp(op): N = op.shape[0] dct = {(N * l, N * k): sm.SparseMatrix(N, N, {(i, i): op[k, l] for i in range(N)}) for k, l in product(range(N), repeat=2)} SpostS = sm.SparseMatrix(N**2, N**2, dct) return SpostS
def makeMESymb(H_L, c_opL=[], e_opL=[], rhoS=None, bReturnMatrixEquation=False): """Take the Hamiltonia,coeficients and return density matrix evolution expressions Format for H: [H0, [coeff_sym1, H1], [coeff_sym2, H2] ...]""" #print('makeMESymb enter', flush=True) #pdb.set_trace() # Make the liouvillian----------------------------------- H0 = H_L.pop(0) H0 = sm.SparseMatrix(H0) Nstates = H0.shape[0] if rhoS is None: rhoS = getRhoS(Nstates) # Construct Hamiltonian part L = liouvillianS(H0) for el in H_L: if np.iterable(el) and not type(el) == q.Qobj and not type( el) == np.matrix and not type(el) == np.array: sym, op = el else: print( "no coefficient for a hamiltonian element. Will assume it's 1") sym = 1. op = sm.SparseMatrix(el) L += sym * liouvillianS(op) for el in c_opL[:]: if np.iterable(el) and not type(el) == q.Qobj and not type( el) == np.matrix and not type(el) == np.array: coef, op = el else: coef = 1. op = el op = sm.SparseMatrix(op) L += coef * liouvillianS(None, cOpL=[op]) # Get drho_dt drho_dtS = (L * rhoS.reshape(Nstates**2, 1)).reshape( Nstates, Nstates) # *sm.Matrix(rs) drho_dtS.simplify() e_op_outL = [] for op in e_opL: ex = sm.Trace(rhoS * op).doit( ) # np.sum(array(H1[off_diag_ind])*array(rhoS)[off_diag_ind]).subs(Esym,1) ex = ex.simplify() e_op_outL.append(ex) eq = sm.Eq(rhoS, drho_dtS) if bReturnMatrixEquation: #returns the full matrix return eq, e_op_outL lhsL, rhsL = seperate_DM_equation(eq) #otherwise just the evolving bits return lhsL, rhsL, e_op_outL
def compute_Jwx(self): if False: self.Jwx = sy.SparseMatrix( sy.zeros(self.N + 1, self.Nxi * self.N - self.N_lambda)) self.Jwx[0:self.N + 1, 0:self.N + 1] = sy.SparseMatrix(sy.eye(self.N + 1)) # terminal dissipation self.Jwx = sy.zeros(1, self.Nxi * self.N - self.N_lambda) self.Jwx[0, 2 * self.N - self.N_lambda - 1] = 1 print(self.Jwx)
def compute_Jyx(self): self.Jyx = sy.SparseMatrix( sy.zeros(self.N + 2, self.Nx - self.N_lambda)) self.Jyx[0, 0] = -1 self.Jyx[1, 2 * self.N - self.N_lambda - 1] = 1 self.Jyx[2::, 2 * self.N - self.N_lambda:3 * self.N - self.N_lambda] = -1 * sy.eye(self.N)
def pd(self): output = sympify(0) for w, z in zip(self.w, self.z): output += w * z output += sympy.SparseMatrix(self.a()).dot( matvecprod(self.R(), self.a())) return output
def _print_Identity(self, expr): if isinstance(expr.rows, Integer) or isinstance( expr.rows, IntegerConstant): return self._print(sympy.SparseMatrix(expr)) else: return "Matrix({var_size}, shape = identity)".format( var_size=self._print(expr.rows))
def compute_variableChange(self): ''' Goes from a DAE-PHS formulation to a constrainted pHs formulation''' self.compute_B() self.compute_B_left_annihilator() self.M = sy.SparseMatrix(sy.zeros(self.Nx, self.Nx)) self.M[0:self.Nx- self.N_lambda,::] = self.annul_b self.M[self.Nx- self.N_lambda::, ::] = (self.b.T*self.b).inv()*self.b.T
def matrix_coeff(m, s): """returns the sp.Matrix valued coefficients N_i in m(x) = N_1 * x**(n-1) + N_2 * x**(n-2) + .. + N_deg(m) m : sp.Matrix sp.Matrix to get coefficient matrices from s : sp.Symbol to compute coefficient list (coefficients are ambiguous for expressins with multiple symbols) """ m_deg = matrix_degree(m, s) res = [sp.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 = sp.Poly(entry, s).all_coeffs() if sp.simplify(entry) == 0: coeff_deg = 0 else: coeff_deg = sp.degree(entry, s) for c, coeff in enumerate(entry_coeff_list): res[c + m_deg - coeff_deg] += \ sp.SparseMatrix(m.shape[0], m.shape[1], {(r, e): 1}) * coeff return res
def compute_B_left_annihilator(self): self.annul_b = sy.SparseMatrix(sy.zeros(self.Nx-self.N_lambda, self.Nx)) self.annul_b[0,0] = 1 self.annul_b[self.N::, self.N + self.N_lambda::] = sy.eye(3*self.N+1) for i in range(1,self.N_lambda+1): self.annul_b[i, 2*i-1] = 1 self.annul_b[i, 2*i] = 1
def get_all_Jacobians(self): size = len(self.frames) # j shape will always be (6 x size) J_rows = 6 * size J_cols = size J = sym.SparseMatrix(J_rows, J_cols, [0] * J_rows * J_cols) for i in range(size): J[i * 6:(i + 1) * 6, 0:size] = self.get_Jacobian(i + 1) return J
def compute_Jxx(self): self.Jxx = sy.SparseMatrix(sy.zeros(self.Nx)) for i in range(self.N): self.Jxx[self.Nxi*i:self.Nxi*(i+1), self.Nxi*i:self.Nxi*(i+1)] = self.compute_Ji(i+1) self.compute_permutation_matrix() self.compute_variableChange() self.Jxx = self.M*self.P * self.Jxx * self.P.T * self.M.T self.Jxx = self.Jxx[0:self.Nx-self.N_lambda, 0:self.Nx-self.N_lambda]
def __init__(self, model, with_jacobian=False, cleanup=True, _logger=None): self.with_jacobian = with_jacobian self.cleanup = cleanup self._logger = _logger expr_dynamic = model.expressions_dynamic(include_derived=True) expr_constant = model.expressions_constant(include_derived=True) self.y = sympy.MatrixSymbol('y', len(model.species), 1) self.p = sympy.MatrixSymbol('p', len(model.parameters_all()), 1) self.e = sympy.MatrixSymbol('e', len(expr_constant), 1) self.o = sympy.MatrixSymbol('o', len(model.observables), 1) # Parameters symbols. We also need this independently of all_subs. param_subs = dict(zip(model.parameters_all(), self.p)) # All substitution rules we need to apply to the reaction expressions. all_subs = param_subs.copy() # Species symbols. all_subs.update({ sympy.Symbol('__s%d' % i): self.y[i] for i in range(len(model.species)) }) # Observables symbols. all_subs.update(dict(zip(model.observables, self.o))) # Constant expressions symbols. all_subs.update(dict(zip(expr_constant, self.e))) # Dynamic expressions (expanded). all_subs.update({e: e.expand_expr() for e in expr_dynamic}) self.kinetics = sympy.Matrix( [r['rate'].subs(all_subs) for r in model.reactions]) if with_jacobian: # The Jacobian can be quite large but it's extremely sparse. We can # obtain a sparse representation by making a SparseMatrix copy of # the kinetics vector and computing the Jacobian on that. kinetics_sparse = sympy.SparseMatrix(self.kinetics) # Rather than substitute all the observables linear-combination-of- # species expressions into the kinetics and then compute the # Jacobian, we can use the total derivative / chain rule to # simplify things. The final jacobian of the kinetics must be # computed as jac(y) + jac(o) * observables_matrix. self._logger.debug("Computing Jacobian matrices") self.kinetics_jacobian_y = kinetics_sparse.jacobian(self.y) self.kinetics_jacobian_o = kinetics_sparse.jacobian(self.o) obs_matrix = scipy.sparse.lil_matrix( (len(model.observables), len(model.species)), dtype=np.int64) for i, obs in enumerate(model.observables): obs_matrix[i, obs.species] = obs.coefficients self.observables_matrix = obs_matrix.tocsr() self.stoichiometry_matrix = model.stoichiometry_matrix self.model_name = model.name self._expressions_constant = [ e.expand_expr().xreplace(param_subs) for e in expr_constant ] self._work_path = None self._expressions_constant_fn = None self._rhs_fn = None self._jacobian_fn = None
def hermitian_basis(dim): """ Returns a basis of the hermitian matrices of size ``dim`` that is orthogonal w.r.t. the Frobenius scalar product. :param dim: size of the matrices :type dim: int Example: >>> import kdotp_symmetry as kp >>> kp.hermitian_basis(2) [Matrix([ [1, 0], [0, 0]]), Matrix([ [0, 0], [0, 1]]), Matrix([ [0, 1], [1, 0]]), Matrix([ [0, -I], [I, 0]])] """ basis = [] # diagonal entries for i in range(dim): basis.append(sp.SparseMatrix(dim, dim, {(i, i): 1})) # off-diagonal entries for i in range(dim): for j in range(i + 1, dim): # real basis.append(sp.SparseMatrix(dim, dim, {(i, j): 1, (j, i): 1})) # imag basis.append( sp.SparseMatrix(dim, dim, {(i, j): -sp.I, (j, i): sp.I}) ) assert len(basis) == dim**2 return basis
def compute_Q12(self): self.Q12 = sy.zeros(self.N_lambda, self.N+1) for i in range(self.N_lambda): for j in range(self.N+1): if i == j: self.Q12[i, j] = -sy.Rational(1,2) * self.mu(i+1) if j == i+1: self.Q12[i, j] = sy.Rational(1,2) * self.mu_minus_mu(i+1,i+2) if j == i+2: self.Q12[i, j] = sy.Rational(1,2) * self.mu(i+2) self.Q12 = sy.SparseMatrix(self.Q12) return self.Q12
def liouvillianS(op, cOpL=[]): if op is not None: L = 1j * (SpreSmSp(op) - SpostSmSp((op))) else: N = cOpL[0].shape[0] #L = np.matrix(np.zeros((N**2,N**2), dtype='object')) L = sm.SparseMatrix(N**2, N**2, {}) for cOp in cOpL: #cOp = np.matrix(cOp) cdc = cOp.H * cOp #cdc = herm_c(cOp)*cOp L += SpreSmSp(cOp)*SpostSmSp(cOp.H) -\ (SpreSmSp(cdc) +SpostSmSp(cdc))/2 return L
def smith_normal_form(matrix, augment=None): """Computes the Smith normal form of the given matrix. Args: matrix: augment: Returns: n x n smith normal form of the matrix. Particularly for projection onto the nullspace of M and the orthogonal complement that is, for a matrix M, P = _smith_normal_form(M) is a projection operator onto the nullspace of M """ if augment is not None: M = matrix.row_join(augment) k = augment.cols else: M = matrix k = 0 m, n = M.shape M = augmented_rref(M, k) Mp = sympy.MutableSparseMatrix(n - k, n, {}) constraints = [] for row in range(m): leading_coeff = -1 for col in range(row, n - k): if M[row, col] != 0: leading_coeff = col break if leading_coeff < 0: if not M[row, n - k:].is_zero_matrix: constraints.append(sum(M[row, :])) else: Mp[leading_coeff, :] = M[row, :] if augment: return Mp[:, :-k], Mp[:, -k:], constraints else: return Mp, sympy.SparseMatrix(m, k, {}), constraints
def compute_param_custom(self): """Compute `self.Y` for lines""" # Note: this one assumes `self.a1_int` is the indices of `self.a1` in `bus.a` dok_y = dict() a1_addr = self._dae_address['a1'] a2_addr = self._dae_address['a2'] # pylint: disable=maybe-no-member for a1, y1, m2, y12 in zip(a1_addr, self.y1, self.m2, self.y12): # Need to check if key exist. Otherwise, same multiple `a1` will overwrite the value if (a1, a1) not in dok_y.keys(): dok_y[(a1, a1)] = (y1 + y12) / m2 else: dok_y[(a1, a1)] = dok_y[(a1, a1)] + ((y1 + y12) / m2) for a1, a2, y12, mconj in zip(a1_addr, a2_addr, self.y12, self.mconj): if (a1, a2) not in dok_y.keys(): dok_y[(a1, a2)] = -y12 / mconj else: dok_y[(a1, a2)] = dok_y[(a1, a2)] - y12 / mconj for a1, a2, y12, m in zip(a1_addr, a2_addr, self.y12, self.m): if (a2, a1) not in dok_y.keys(): dok_y[(a2, a1)] = -y12 / m else: dok_y[(a2, a1)] = dok_y[(a2, a1)] - y12 / m for a2, y12, y2 in zip(a2_addr, self.y12, self.y2): if (a2, a2) not in dok_y.keys(): dok_y[(a2, a2)] = y12 + y2 else: dok_y[(a2, a2)] = dok_y[(a2, a2)] + (y12 + y2) nbus = self.system.bus.n self.Y = smp.SparseMatrix(nbus, nbus, dok_y) self.Y = smp.Matrix(self.Y)
def processReactions(self): """Get reactions from SBML model. Arguments: Returns: Raises: """ reactions = self.sbml.getListOfReactions() nr = len(reactions) nx = len(self.symbols['species']['name']) # stoichiometric matrix self.stoichiometricMatrix = sp.SparseMatrix(sp.zeros(nx, nr)) self.fluxVector = sp.zeros(nr, 1) assignment_ids = [ ass.getId() for ass in self.sbml.getListOfInitialAssignments() ] rulevars = [ rule.getVariable() for rule in self.sbml.getListOfRules() if rule.getFormula() != '' ] reaction_ids = [ reaction.getId() for reaction in reactions if reaction.isSetId() ] def getElementFromAssignment(element_id): assignment = self.sbml.getInitialAssignment(element_id) sym = sp.sympify(sbml.formulaToL3String(assignment.getMath())) # this is an initial assignment so we need to use # initial conditions if sym is not None: sym = sym.subs(self.symbols['species']['identifier'], self.symbols['species']['value']) return sym def getElementStoichiometry(element): if element.isSetId(): if element.getId() in assignment_ids: symMath = getElementFromAssignment(element.getId()) if symMath is None: symMath = sp.sympify(element.getStoichiometry()) elif element.getId() in rulevars: return sp.Symbol(element.getId()) else: # dont put the symbol if it wont get replaced by a # rule symMath = sp.sympify(element.getStoichiometry()) elif element.isSetStoichiometry(): symMath = sp.sympify(element.getStoichiometry()) else: return sp.sympify(1.0) _check_unsupported_functions(symMath, 'Stoichiometry') return symMath def isConstant(specie): return specie in self.constantSpecies or \ specie in self.boundaryConditionSpecies for reactionIndex, reaction in enumerate(reactions): for elementList, sign in [(reaction.getListOfReactants(), -1.0), (reaction.getListOfProducts(), 1.0)]: elements = {} for index, element in enumerate(elementList): # we need the index here as we might have multiple elements # for the same species elements[index] = {'species': element.getSpecies()} elements[index]['stoichiometry'] = getElementStoichiometry( element) for index in elements.keys(): if not isConstant(elements[index]['species']): specieIndex = self.speciesIndex[elements[index] ['species']] self.stoichiometricMatrix[specieIndex, reactionIndex] \ += sign \ * elements[index]['stoichiometry'] \ * self.speciesConversionFactor[specieIndex] \ / self.speciesCompartment[specieIndex] # usage of formulaToL3String ensures that we get "time" as time # symbol math = sbml.formulaToL3String(reaction.getKineticLaw().getMath()) try: symMath = sp.sympify(math) except: raise SBMLException(f'Kinetic law "{math}" contains an ' 'unsupported expression!') _check_unsupported_functions(symMath, 'KineticLaw') for r in reactions: elements = list(r.getListOfReactants()) \ + list(r.getListOfProducts()) for element in elements: if element.isSetId() & element.isSetStoichiometry(): symMath = symMath.subs( sp.sympify(element.getId()), sp.sympify(element.getStoichiometry())) self.fluxVector[reactionIndex] = symMath if any([ str(symbol) in reaction_ids for symbol in self.fluxVector[reactionIndex].free_symbols ]): raise SBMLException( 'Kinetic laws involving reaction ids are currently' ' not supported!')
def system_model(self, control_vars=None): """Produces a symbolic model of the system in reduced form. In many cases it is useful to have a full description of the system in symbolic form, and not just a list of constitutive relations. Returns: (coordinates, mappings, linear_op, nonlinear_op, constraints) This method generates: * The model coordinate system (`list`) :math:`x` * A mapping (`dict`) between the model coordinates and the component coordinates * A linear operator (`sympy.Matrix`) :math:`L` * A nonlinear operator (`sympy.Matrix`) :math:`F` * A list of constraints (`sympy.Matrix`) :math:`G` The coordinates are of the form .. math:: x = (dx_0, dx_1, \\ldots, e_0, f_0, e_1, f_1, \\ldots, x_0, x_1, \\ldots, u_0, u_1, \\ldots) So that the system obeys the differential-algebraic equation .. math:: Lx + F(x) = 0 \\qquad G(x) =0 See Also: :attr:`BondGraph.basis_vectors` """ mappings, coordinates = inverse_coord_maps( *self._build_internal_basis_vectors()) inv_tm, inv_js, inv_cv = mappings network_size = len(inv_js) # number of ports state_size = len(inv_tm) # number of state space coords inout_size = len(inv_cv) n = len(coordinates) size_tuple = (state_size, network_size, inout_size, n) lin_dict = adjacency_to_dict(inv_js, self.bonds, offset=state_size) nlin_dict = {} try: row = max(row + 1 for row, _ in lin_dict.keys()) except ValueError: row = 0 inverse_port_map = {} for port, (cv_e, cv_f) in self._port_map.items(): inverse_port_map[cv_e] = state_size + 2 * inv_js[port] inverse_port_map[cv_f] = state_size + 2 * inv_js[port] + 1 for component in self.components: relations = get_relations_iterator(component, mappings, coordinates, inverse_port_map) for linear, nonlinear in relations: lin_dict.update({(row, k): v for k, v in linear.items()}) nlin_dict.update({(row, 0): nonlinear}) row += 1 linear_op = sp.SparseMatrix(row, n, lin_dict) nonlinear_op = sp.SparseMatrix(row, 1, nlin_dict) coordinates, linear_op, nonlinear_op, constraints = reduce_model( linear_op, nonlinear_op, coordinates, size_tuple, control_vars=control_vars) return coordinates, mappings, linear_op, nonlinear_op, constraints