def test_linear_eq_to_matrix(): x, y, z = symbols('x, y, z') eqns1 = [2 * x + y - 2 * z - 3, x - y - z, x + y + 3 * z - 12] eqns2 = [ Eq(3 * x + 2 * y - z, 1), Eq(2 * x - 2 * y + 4 * z, -2), -2 * x + y - 2 * z ] A, b = linear_eq_to_matrix(eqns1, x, y, z) assert A == Matrix([[2, 1, -2], [1, -1, -1], [1, 1, 3]]) assert b == Matrix([[3], [0], [12]]) A, b = linear_eq_to_matrix(eqns2, x, y, z) assert A == Matrix([[3, 2, -1], [2, -2, 4], [-2, 1, -2]]) assert b == Matrix([[1], [-2], [0]]) # Pure symbolic coefficients from sympy.abc import a, b, c, d, e, f, g, h, i, j, k, l eqns3 = [ a * x + b * y + c * z - d, e * x + f * y + g * z - h, i * x + j * y + k * z - l ] A, B = linear_eq_to_matrix(eqns3, x, y, z) assert A == Matrix([[a, b, c], [e, f, g], [i, j, k]]) assert B == Matrix([[d], [h], [l]]) # raise ValueError if no symbols are given raises(ValueError, lambda: linear_eq_to_matrix(eqns3))
def test_linear_eq_to_matrix(): x, y, z = symbols('x, y, z') eqns1 = [2*x + y - 2*z - 3, x - y - z, x + y + 3*z - 12] eqns2 = [Eq(3*x + 2*y - z, 1), Eq(2*x - 2*y + 4*z, -2), -2*x + y - 2*z] A, b = linear_eq_to_matrix(eqns1, x, y, z) assert A == Matrix([[2, 1, -2], [1, -1, -1], [1, 1, 3]]) assert b == Matrix([[3], [0], [12]]) A, b = linear_eq_to_matrix(eqns2, x, y, z) assert A == Matrix([[3, 2, -1], [2, -2, 4], [-2, 1, -2]]) assert b == Matrix([[1], [-2], [0]]) # Pure symbolic coefficients from sympy.abc import a, b, c, d, e, f, g, h, i, j, k, l eqns3 = [a*x + b*y + c*z - d, e*x + f*y + g*z - h, i*x + j*y + k*z - l] A, B = linear_eq_to_matrix(eqns3, x, y, z) assert A == Matrix([[a, b, c], [e, f, g], [i, j, k]]) assert B == Matrix([[d], [h], [l]]) # raise ValueError if no symbols are given raises(ValueError, lambda: linear_eq_to_matrix(eqns3))
def linsolve(system, *symbols): if not system: return S.EmptySet # If second argument is an iterable if symbols and hasattr(symbols[0], '__iter__'): symbols = symbols[0] sym_gen = isinstance(symbols, GeneratorType) swap = {} b = None # if we don't get b the input was bad syms_needed_msg = None # unpack system if hasattr(system, '__iter__'): # 1). (A, b) if len(system) == 2 and isinstance(system[0], Matrix): A, b = system # 2). (eq1, eq2, ...) if not isinstance(system[0], Matrix): if sym_gen or not symbols: raise ValueError( filldedent(''' When passing a system of equations, the explicit symbols for which a solution is being sought must be given as a sequence, too. ''')) system = [ _mexpand(i.lhs - i.rhs if isinstance(i, Eq) else i, recursive=True) for i in system ] system, symbols, swap = recast_to_symbols(system, symbols) A, b = linear_eq_to_matrix(system, symbols) syms_needed_msg = 'free symbols in the equations provided' elif isinstance(system, Matrix) and not (symbols and not isinstance( symbols, GeneratorType) and isinstance(symbols[0], Matrix)): # 3). A augmented with b A, b = system[:, :-1], system[:, -1:] if b is None: raise ValueError("Invalid arguments") syms_needed_msg = syms_needed_msg or 'columns of A' if sym_gen: symbols = [next(symbols) for i in range(A.cols)] if any(set(symbols) & (A.free_symbols | b.free_symbols)): raise ValueError( filldedent(''' At least one of the symbols provided already appears in the system to be solved. One way to avoid this is to use Dummy symbols in the generator, e.g. numbered_symbols('%s', cls=Dummy) ''' % symbols[0].name.rstrip('1234567890'))) try: solution, params, free_syms = A.gauss_jordan_solve(b, freevar=True) except ValueError: # No solution return S.EmptySet # Replace free parameters with free symbols if params: if not symbols: symbols = [_ for _ in params] # re-use the parameters but put them in order # params [x, y, z] # free_symbols [2, 0, 4] # idx [1, 0, 2] idx = list(zip(*sorted(zip(free_syms, range(len(free_syms))))))[1] # simultaneous replacements {y: x, x: y, z: z} replace_dict = dict(zip(symbols, [symbols[i] for i in idx])) elif len(symbols) >= A.cols: replace_dict = { v: symbols[free_syms[k]] for k, v in enumerate(params) } else: raise IndexError( filldedent(''' the number of symbols passed should have a length equal to the number of %s. ''' % syms_needed_msg)) solution = [sol.xreplace(replace_dict) for sol in solution] solution = [(sol).xreplace(swap) for sol in solution] return FiniteSet(tuple(solution))
def linear_ode_to_matrix(eqs, funcs, t, order): r""" Convert a linear system of ODEs to matrix form Explanation =========== Express a system of linear ordinary differential equations as a single matrix differential equation [1]. For example the system $x' = x + y + 1$ and $y' = x - y$ can be represented as .. math:: A_1 X' + A_0 X = b where $A_1$ and $A_0$ are $2 \times 2$ matrices and $b$, $X$ and $X'$ are $2 \times 1$ matrices with $X = [x, y]^T$. Higher-order systems are represented with additional matrices e.g. a second-order system would look like .. math:: A_2 X'' + A_1 X' + A_0 X = b Examples ======== >>> from sympy import (Function, Symbol, Matrix, Eq) >>> from sympy.solvers.ode.systems import linear_ode_to_matrix >>> t = Symbol('t') >>> x = Function('x') >>> y = Function('y') We can create a system of linear ODEs like >>> eqs = [ ... Eq(x(t).diff(t), x(t) + y(t) + 1), ... Eq(y(t).diff(t), x(t) - y(t)), ... ] >>> funcs = [x(t), y(t)] >>> order = 1 # 1st order system Now ``linear_ode_to_matrix`` can represent this as a matrix differential equation. >>> (A1, A0), b = linear_ode_to_matrix(eqs, funcs, t, order) >>> A1 Matrix([ [1, 0], [0, 1]]) >>> A0 Matrix([ [-1, -1], [-1, 1]]) >>> b Matrix([ [1], [0]]) The original equations can be recovered from these matrices: >>> eqs_mat = Matrix([eq.lhs - eq.rhs for eq in eqs]) >>> X = Matrix(funcs) >>> A1 * X.diff(t) + A0 * X - b == eqs_mat True If the system of equations has a maximum order greater than the order of the system specified, a ODEOrderError exception is raised. >>> eqs = [Eq(x(t).diff(t, 2), x(t).diff(t) + x(t)), Eq(y(t).diff(t), y(t) + x(t))] >>> linear_ode_to_matrix(eqs, funcs, t, 1) Traceback (most recent call last): ... ODEOrderError: Cannot represent system in 1-order form If the system of equations is nonlinear, then ODENonlinearError is raised. >>> eqs = [Eq(x(t).diff(t), x(t) + y(t)), Eq(y(t).diff(t), y(t)**2 + x(t))] >>> linear_ode_to_matrix(eqs, funcs, t, 1) Traceback (most recent call last): ... ODENonlinearError: The system of ODEs is nonlinear. Parameters ========== eqs : list of sympy expressions or equalities The equations as expressions (assumed equal to zero). funcs : list of applied functions The dependent variables of the system of ODEs. t : symbol The independent variable. order : int The order of the system of ODEs. Returns ======= The tuple ``(As, b)`` where ``As`` is a tuple of matrices and ``b`` is the the matrix representing the rhs of the matrix equation. Raises ====== ODEOrderError When the system of ODEs have an order greater than what was specified ODENonlinearError When the system of ODEs is nonlinear See Also ======== linear_eq_to_matrix: for systems of linear algebraic equations. References ========== .. [1] https://en.wikipedia.org/wiki/Matrix_differential_equation """ from sympy.solvers.solveset import linear_eq_to_matrix if any(ode_order(eq, func) > order for eq in eqs for func in funcs): msg = "Cannot represent system in {}-order form" raise ODEOrderError(msg.format(order)) As = [] for o in range(order, -1, -1): # Work from the highest derivative down funcs_deriv = [func.diff(t, o) for func in funcs] # linear_eq_to_matrix expects a proper symbol so substitute e.g. # Derivative(x(t), t) for a Dummy. rep = {func_deriv: Dummy() for func_deriv in funcs_deriv} eqs = [eq.subs(rep) for eq in eqs] syms = [rep[func_deriv] for func_deriv in funcs_deriv] # Ai is the matrix for X(t).diff(t, o) # eqs is minus the remainder of the equations. try: Ai, b = linear_eq_to_matrix(eqs, syms) except NonlinearError: raise ODENonlinearError("The system of ODEs is nonlinear.") As.append(Ai) if o: eqs = [-eq for eq in b] else: rhs = b return As, rhs
def _symcheck(): # import sympy as sym # # Create the index for our indexable sequence objects # i = sym.symbols('i', cls=sym.Idx) # measured = list(map(sym.IndexedBase, ['A_m', 'B_m', 'C_m'])) # smoothed = list(map(sym.IndexedBase, ['A_s', 'B_s', 'C_s'])) # for S in smoothed: # S[0] = 0 # for M, S in zip(measured, smoothed): # M[i] # pass # for r in raw_values: # z = r[i] # .is_nonnegative = True # r.is_real = True ########### # I'm not sure how to used index objects correctly, so lets hack it import sympy as sym kw = dict(is_nonnegative=True, is_real=True) measured = {'A_m': [], 'B_m': [], 'C_m': [], 'T_m': []} smoothed = {'A_s': [], 'B_s': [], 'C_s': [], 'T_s': []} for i in range(3): _syms = sym.symbols('A_m{i}, B_m{i}, C_m{i}, T_m{i}'.format(i=i), **kw) for _sym in _syms: prefix = _sym.name[0:-1] measured[prefix].append(_sym) _syms = sym.symbols('A_s{i}, B_s{i}, C_s{i}, T_s{i}'.format(i=i), **kw) for _sym in _syms: prefix = _sym.name[0:-1] smoothed[prefix].append(_sym) # alpha, beta = sym.symbols('α, β', is_real=1, is_positive=True) # constraints.append(sym.Eq(beta, 1 - alpha)) # constraints.append(sym.Le(alpha, 1)) # constraints.append(sym.Ge(alpha, 0)) constraints = [] N = 2 alpha = 0.6 beta = 1 - 0.6 for i in range(N): # Total measure constraints constr = sym.Eq( measured['T_m'][i], measured['A_m'][i] + measured['B_m'][i] + measured['C_m'][i]) # EWMA constraints for s_key in smoothed.keys(): m_key = s_key.replace('_s', '_m') S = smoothed[s_key] M = measured[m_key] if i == 0: constr = sym.Eq(S[i], M[i]) constraints.append(constr) else: constr = sym.Eq(S[i], M[i] * alpha + beta * S[i - 1]) constraints.append(constr) constr = sym.Eq( measured['T_m'][i], measured['A_m'][i] + measured['B_m'][i] + measured['C_m'][i]) constraints.append(constr) from sympy.solvers.solveset import linear_eq_to_matrix from sympy.solvers.solveset import linsolve import itertools as it import ubelt as ub all_symbols = list( ub.flatten(it.chain(measured.values(), smoothed.values()))) # WE WANT TO KNOW IF THIS IS POSSIBLE constraints.append(sym.Gt(measured['A_m'][-1], measured['T_m'][-1]), ) A, b = linear_eq_to_matrix(constraints, all_symbols) linsolve((A, b))