def reflect(self, line): """Override GeometryEntity.reflect since the radius is not a GeometryEntity. Examples ======== >>> from sympy import Circle, Line >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) Circle(Point2D(1, 0), -1) >>> from sympy import Ellipse, Line, Point >>> Ellipse(Point(3, 4), 1, 3).reflect(Line(Point(0, -4), Point(5, 0))) Traceback (most recent call last): ... NotImplementedError: General Ellipse is not supported but the equation of the reflected Ellipse is given by the zeros of: f(x, y) = (9*x/41 + 40*y/41 + 37/41)**2 + (40*x/123 - 3*y/41 - 364/123)**2 - 1 Notes ===== Until the general ellipse (with no axis parallel to the x-axis) is supported a NotImplemented error is raised and the equation whose zeros define the rotated ellipse is given. """ if line.slope in (0, oo): c = self.center c = c.reflect(line) return self.func(c, -self.hradius, self.vradius) else: x, y = [ _uniquely_named_symbol(name, (self, line), real=True) for name in 'xy' ] expr = self.equation(x, y) p = Point(x, y).reflect(line) result = expr.subs(zip((x, y), p.args), simultaneous=True) raise NotImplementedError( filldedent( 'General Ellipse is not supported but the equation ' 'of the reflected Ellipse is given by the zeros of: ' + "f(%s, %s) = %s" % (str(x), str(y), str(result))))
def reflect(self, line): """Override GeometryEntity.reflect since the radius is not a GeometryEntity. Examples ======== >>> from sympy import Circle, Line >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) Circle(Point2D(1, 0), -1) >>> from sympy import Ellipse, Line, Point >>> Ellipse(Point(3, 4), 1, 3).reflect(Line(Point(0, -4), Point(5, 0))) Traceback (most recent call last): ... NotImplementedError: General Ellipse is not supported but the equation of the reflected Ellipse is given by the zeros of: f(x, y) = (9*x/41 + 40*y/41 + 37/41)**2 + (40*x/123 - 3*y/41 - 364/123)**2 - 1 Notes ===== Until the general ellipse (with no axis parallel to the x-axis) is supported a NotImplemented error is raised and the equation whose zeros define the rotated ellipse is given. """ if line.slope in (0, oo): c = self.center c = c.reflect(line) return self.func(c, -self.hradius, self.vradius) else: x, y = [_uniquely_named_symbol( name, (self, line), real=True) for name in 'xy'] expr = self.equation(x, y) p = Point(x, y).reflect(line) result = expr.subs(zip((x, y), p.args ), simultaneous=True) raise NotImplementedError(filldedent( 'General Ellipse is not supported but the equation ' 'of the reflected Ellipse is given by the zeros of: ' + "f(%s, %s) = %s" % (str(x), str(y), str(result))))
def _preprocess(self, args, expr): """Preprocess args, expr to replace arguments that do not map to valid Python identifiers. Returns string form of args, and updated expr. """ from sympy import Dummy, Function, flatten, Derivative, ordered, Basic from sympy.matrices import DeferredVector from sympy.core.symbol import _uniquely_named_symbol from sympy.core.expr import Expr # Args of type Dummy can cause name collisions with args # of type Symbol. Force dummify of everything in this # situation. dummify = self._dummify or any( isinstance(arg, Dummy) for arg in flatten(args)) argstrs = [None] * len(args) for arg, i in reversed(list(ordered(zip(args, range(len(args)))))): if iterable(arg): s, expr = self._preprocess(arg, expr) elif isinstance(arg, DeferredVector): s = str(arg) elif isinstance(arg, Basic) and arg.is_symbol: s = self._argrepr(arg) if dummify or not self._is_safe_ident(s): dummy = Dummy() if isinstance(expr, Expr): dummy = _uniquely_named_symbol(dummy.name, expr) s = self._argrepr(dummy) expr = self._subexpr(expr, {arg: dummy}) elif dummify or isinstance(arg, (Function, Derivative)): dummy = Dummy() s = self._argrepr(dummy) expr = self._subexpr(expr, {arg: dummy}) else: s = str(arg) argstrs[i] = s return argstrs, expr
def _gauss_jordan_solve(M, B, freevar=False): """ Solves ``Ax = B`` using Gauss Jordan elimination. There may be zero, one, or infinite solutions. If one solution exists, it will be returned. If infinite solutions exist, it will be returned parametrically. If no solutions exist, It will throw ValueError. Parameters ========== B : Matrix The right hand side of the equation to be solved for. Must have the same number of rows as matrix A. freevar : List If the system is underdetermined (e.g. A has more columns than rows), infinite solutions are possible, in terms of arbitrary values of free variables. Then the index of the free variables in the solutions (column Matrix) will be returned by freevar, if the flag `freevar` is set to `True`. Returns ======= x : Matrix The matrix that will satisfy ``Ax = B``. Will have as many rows as matrix A has columns, and as many columns as matrix B. params : Matrix If the system is underdetermined (e.g. A has more columns than rows), infinite solutions are possible, in terms of arbitrary parameters. These arbitrary parameters are returned as params Matrix. Examples ======== >>> from sympy import Matrix >>> A = Matrix([[1, 2, 1, 1], [1, 2, 2, -1], [2, 4, 0, 6]]) >>> B = Matrix([7, 12, 4]) >>> sol, params = A.gauss_jordan_solve(B) >>> sol Matrix([ [-2*tau0 - 3*tau1 + 2], [ tau0], [ 2*tau1 + 5], [ tau1]]) >>> params Matrix([ [tau0], [tau1]]) >>> taus_zeroes = { tau:0 for tau in params } >>> sol_unique = sol.xreplace(taus_zeroes) >>> sol_unique Matrix([ [2], [0], [5], [0]]) >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 10]]) >>> B = Matrix([3, 6, 9]) >>> sol, params = A.gauss_jordan_solve(B) >>> sol Matrix([ [-1], [ 2], [ 0]]) >>> params Matrix(0, 1, []) >>> A = Matrix([[2, -7], [-1, 4]]) >>> B = Matrix([[-21, 3], [12, -2]]) >>> sol, params = A.gauss_jordan_solve(B) >>> sol Matrix([ [0, -2], [3, -1]]) >>> params Matrix(0, 2, []) See Also ======== sympy.matrices.dense.DenseMatrix.lower_triangular_solve sympy.matrices.dense.DenseMatrix.upper_triangular_solve cholesky_solve diagonal_solve LDLsolve LUsolve QRsolve pinv References ========== .. [1] https://en.wikipedia.org/wiki/Gaussian_elimination """ from sympy.matrices import Matrix, zeros cls = M.__class__ aug = M.hstack(M.copy(), B.copy()) B_cols = B.cols row, col = aug[:, :-B_cols].shape # solve by reduced row echelon form A, pivots = aug.rref(simplify=True) A, v = A[:, :-B_cols], A[:, -B_cols:] pivots = list(filter(lambda p: p < col, pivots)) rank = len(pivots) # Bring to block form permutation = Matrix(range(col)).T for i, c in enumerate(pivots): permutation.col_swap(i, c) # check for existence of solutions # rank of aug Matrix should be equal to rank of coefficient matrix if not v[rank:, :].is_zero_matrix: raise ValueError("Linear system has no solution") # Get index of free symbols (free parameters) # non-pivots columns are free variables free_var_index = permutation[len(pivots):] # Free parameters # what are current unnumbered free symbol names? name = _uniquely_named_symbol( 'tau', aug, compare=lambda i: str(i).rstrip('1234567890')).name gen = numbered_symbols(name) tau = Matrix([next(gen) for k in range((col - rank) * B_cols) ]).reshape(col - rank, B_cols) # Full parametric solution V = A[:rank, [c for c in range(A.cols) if c not in pivots]] vt = v[:rank, :] free_sol = tau.vstack(vt - V * tau, tau) # Undo permutation sol = zeros(col, B_cols) for k in range(col): sol[permutation[k], :] = free_sol[k, :] sol, tau = cls(sol), cls(tau) if freevar: return sol, tau, free_var_index else: return sol, tau
def _charpoly(M, x='lambda', simplify=_simplify): """Computes characteristic polynomial det(x*I - M) where I is the identity matrix. A PurePoly is returned, so using different variables for ``x`` does not affect the comparison or the polynomials: Parameters ========== x : string, optional Name for the "lambda" variable, defaults to "lambda". simplify : function, optional Simplification function to use on the characteristic polynomial calculated. Defaults to ``simplify``. Examples ======== >>> from sympy import Matrix >>> from sympy.abc import x, y >>> M = Matrix([[1, 3], [2, 0]]) >>> M.charpoly() PurePoly(lambda**2 - lambda - 6, lambda, domain='ZZ') >>> M.charpoly(x) == M.charpoly(y) True >>> M.charpoly(x) == M.charpoly(y) True Specifying ``x`` is optional; a symbol named ``lambda`` is used by default (which looks good when pretty-printed in unicode): >>> M.charpoly().as_expr() lambda**2 - lambda - 6 And if ``x`` clashes with an existing symbol, underscores will be prepended to the name to make it unique: >>> M = Matrix([[1, 2], [x, 0]]) >>> M.charpoly(x).as_expr() _x**2 - _x - 2*x Whether you pass a symbol or not, the generator can be obtained with the gen attribute since it may not be the same as the symbol that was passed: >>> M.charpoly(x).gen _x >>> M.charpoly(x).gen == x False Notes ===== The Samuelson-Berkowitz algorithm is used to compute the characteristic polynomial efficiently and without any division operations. Thus the characteristic polynomial over any commutative ring without zero divisors can be computed. If the determinant det(x*I - M) can be found out easily as in the case of an upper or a lower triangular matrix, then instead of Samuelson-Berkowitz algorithm, eigenvalues are computed and the characteristic polynomial with their help. See Also ======== det """ if not M.is_square: raise NonSquareMatrixError() if M.is_lower or M.is_upper: diagonal_elements = M.diagonal() x = _uniquely_named_symbol(x, diagonal_elements) m = 1 for i in diagonal_elements: m = m * (x - simplify(i)) return PurePoly(m, x) berk_vector = _berkowitz_vector(M) x = _uniquely_named_symbol(x, berk_vector) return PurePoly([simplify(a) for a in berk_vector], x)
def _charpoly(M, x='lambda', simplify=_simplify, dotprodsimp=None): """Computes characteristic polynomial det(x*I - M) where I is the identity matrix. A PurePoly is returned, so using different variables for ``x`` does not affect the comparison or the polynomials: Parameters ========== x : string, optional Name for the "lambda" variable, defaults to "lambda". simplify : function, optional Simplification function to use on the characteristic polynomial calculated. Defaults to ``simplify``, if ``dotprodsimp`` is ``True`` then this is ignored. dotprodsimp : bool, optional Specifies whether intermediate term algebraic simplification is used to control expression blowup during matrix multiplication. If this is true then the simplify function is not used. Examples ======== >>> from sympy import Matrix >>> from sympy.abc import x, y >>> M = Matrix([[1, 3], [2, 0]]) >>> M.charpoly() PurePoly(lambda**2 - lambda - 6, lambda, domain='ZZ') >>> M.charpoly(x) == M.charpoly(y) True >>> M.charpoly(x) == M.charpoly(y) True Specifying ``x`` is optional; a symbol named ``lambda`` is used by default (which looks good when pretty-printed in unicode): >>> M.charpoly().as_expr() lambda**2 - lambda - 6 And if ``x`` clashes with an existing symbol, underscores will be prepended to the name to make it unique: >>> M = Matrix([[1, 2], [x, 0]]) >>> M.charpoly(x).as_expr() _x**2 - _x - 2*x Whether you pass a symbol or not, the generator can be obtained with the gen attribute since it may not be the same as the symbol that was passed: >>> M.charpoly(x).gen _x >>> M.charpoly(x).gen == x False Notes ===== The Samuelson-Berkowitz algorithm is used to compute the characteristic polynomial efficiently and without any division operations. Thus the characteristic polynomial over any commutative ring without zero divisors can be computed. See Also ======== det """ if not M.is_square: raise NonSquareMatrixError() if dotprodsimp: simplify = lambda e: e berk_vector = _berkowitz_vector(M, dotprodsimp=dotprodsimp) x = _uniquely_named_symbol(x, berk_vector) return PurePoly([simplify(a) for a in berk_vector], x)