Source code for sympy.vector.vector

from sympy.core.assumptions import StdFactKB
from sympy.core import S, Pow, sympify
from sympy.core.expr import AtomicExpr, Expr
from sympy.core.compatibility import range, default_sort_key
from sympy import sqrt, ImmutableMatrix as Matrix, Add
from sympy.vector.coordsysrect import CoordSys3D
from sympy.vector.basisdependent import (BasisDependent, BasisDependentAdd,
                                         BasisDependentMul, BasisDependentZero)
from sympy.vector.dyadic import BaseDyadic, Dyadic, DyadicAdd


[docs]class Vector(BasisDependent): """ Super class for all Vector classes. Ideally, neither this class nor any of its subclasses should be instantiated by the user. """ is_Vector = True _op_priority = 12.0 @property def components(self): """ Returns the components of this vector in the form of a Python dictionary mapping BaseVector instances to the corresponding measure numbers. Examples ======== >>> from sympy.vector import CoordSys3D >>> C = CoordSys3D('C') >>> v = 3*C.i + 4*C.j + 5*C.k >>> v.components {C.i: 3, C.j: 4, C.k: 5} """ # The '_components' attribute is defined according to the # subclass of Vector the instance belongs to. return self._components
[docs] def magnitude(self): """ Returns the magnitude of this vector. """ return sqrt(self & self)
[docs] def normalize(self): """ Returns the normalized version of this vector. """ return self / self.magnitude()
[docs] def dot(self, other): """ Returns the dot product of this Vector, either with another Vector, or a Dyadic, or a Del operator. If 'other' is a Vector, returns the dot product scalar (Sympy expression). If 'other' is a Dyadic, the dot product is returned as a Vector. If 'other' is an instance of Del, returns the directional derivative operator as a Python function. If this function is applied to a scalar expression, it returns the directional derivative of the scalar field wrt this Vector. Parameters ========== other: Vector/Dyadic/Del The Vector or Dyadic we are dotting with, or a Del operator . Examples ======== >>> from sympy.vector import CoordSys3D, Del >>> C = CoordSys3D('C') >>> delop = Del() >>> C.i.dot(C.j) 0 >>> C.i & C.i 1 >>> v = 3*C.i + 4*C.j + 5*C.k >>> v.dot(C.k) 5 >>> (C.i & delop)(C.x*C.y*C.z) C.y*C.z >>> d = C.i.outer(C.i) >>> C.i.dot(d) C.i """ # Check special cases if isinstance(other, Dyadic): if isinstance(self, VectorZero): return Vector.zero outvec = Vector.zero for k, v in other.components.items(): vect_dot = k.args[0].dot(self) outvec += vect_dot * v * k.args[1] return outvec from sympy.vector.deloperator import Del if not isinstance(other, Vector) and not isinstance(other, Del): raise TypeError(str(other) + " is not a vector, dyadic or " + "del operator") # Check if the other is a del operator if isinstance(other, Del): def directional_derivative(field): from sympy.vector.functions import directional_derivative return directional_derivative(field, self) return directional_derivative return dot(self, other)
def __and__(self, other): return self.dot(other) __and__.__doc__ = dot.__doc__
[docs] def cross(self, other): """ Returns the cross product of this Vector with another Vector or Dyadic instance. The cross product is a Vector, if 'other' is a Vector. If 'other' is a Dyadic, this returns a Dyadic instance. Parameters ========== other: Vector/Dyadic The Vector or Dyadic we are crossing with. Examples ======== >>> from sympy.vector import CoordSys3D >>> C = CoordSys3D('C') >>> C.i.cross(C.j) C.k >>> C.i ^ C.i 0 >>> v = 3*C.i + 4*C.j + 5*C.k >>> v ^ C.i 5*C.j + (-4)*C.k >>> d = C.i.outer(C.i) >>> C.j.cross(d) (-1)*(C.k|C.i) """ # Check special cases if isinstance(other, Dyadic): if isinstance(self, VectorZero): return Dyadic.zero outdyad = Dyadic.zero for k, v in other.components.items(): cross_product = self.cross(k.args[0]) outer = cross_product.outer(k.args[1]) outdyad += v * outer return outdyad return cross(self, other)
def __xor__(self, other): return self.cross(other) __xor__.__doc__ = cross.__doc__
[docs] def outer(self, other): """ Returns the outer product of this vector with another, in the form of a Dyadic instance. Parameters ========== other : Vector The Vector with respect to which the outer product is to be computed. Examples ======== >>> from sympy.vector import CoordSys3D >>> N = CoordSys3D('N') >>> N.i.outer(N.j) (N.i|N.j) """ # Handle the special cases if not isinstance(other, Vector): raise TypeError("Invalid operand for outer product") elif (isinstance(self, VectorZero) or isinstance(other, VectorZero)): return Dyadic.zero # Iterate over components of both the vectors to generate # the required Dyadic instance args = [] for k1, v1 in self.components.items(): for k2, v2 in other.components.items(): args.append((v1 * v2) * BaseDyadic(k1, k2)) return DyadicAdd(*args)
[docs] def projection(self, other, scalar=False): """ Returns the vector or scalar projection of the 'other' on 'self'. Examples ======== >>> from sympy.vector.coordsysrect import CoordSys3D >>> from sympy.vector.vector import Vector, BaseVector >>> C = CoordSys3D('C') >>> i, j, k = C.base_vectors() >>> v1 = i + j + k >>> v2 = 3*i + 4*j >>> v1.projection(v2) 7/3*C.i + 7/3*C.j + 7/3*C.k >>> v1.projection(v2, scalar=True) 7/3 """ if self.equals(Vector.zero): return S.zero if scalar else Vector.zero if scalar: return self.dot(other) / self.dot(self) else: return self.dot(other) / self.dot(self) * self
@property def _projections(self): """ Returns the components of this vector but the output includes also zero values components. Examples ======== >>> from sympy.vector import CoordSys3D, Vector >>> C = CoordSys3D('C') >>> v1 = 3*C.i + 4*C.j + 5*C.k >>> v1._projections (3, 4, 5) >>> v2 = C.x*C.y*C.z*C.i >>> v2._projections (C.x*C.y*C.z, 0, 0) >>> v3 = Vector.zero >>> v3._projections (0, 0, 0) """ from sympy.vector.operators import _get_coord_sys_from_expr if isinstance(self, VectorZero): return (S(0), S(0), S(0)) base_vec = next(iter(_get_coord_sys_from_expr(self))).base_vectors() return tuple([self.dot(i) for i in base_vec]) def __or__(self, other): return self.outer(other) __or__.__doc__ = outer.__doc__
[docs] def to_matrix(self, system): """ Returns the matrix form of this vector with respect to the specified coordinate system. Parameters ========== system : CoordSys3D The system wrt which the matrix form is to be computed Examples ======== >>> from sympy.vector import CoordSys3D >>> C = CoordSys3D('C') >>> from sympy.abc import a, b, c >>> v = a*C.i + b*C.j + c*C.k >>> v.to_matrix(C) Matrix([ [a], [b], [c]]) """ return Matrix([self.dot(unit_vec) for unit_vec in system.base_vectors()])
[docs] def separate(self): """ The constituents of this vector in different coordinate systems, as per its definition. Returns a dict mapping each CoordSys3D to the corresponding constituent Vector. Examples ======== >>> from sympy.vector import CoordSys3D >>> R1 = CoordSys3D('R1') >>> R2 = CoordSys3D('R2') >>> v = R1.i + R2.i >>> v.separate() == {R1: R1.i, R2: R2.i} True """ parts = {} for vect, measure in self.components.items(): parts[vect.system] = (parts.get(vect.system, Vector.zero) + vect * measure) return parts
class BaseVector(Vector, AtomicExpr): """ Class to denote a base vector. Unicode pretty forms in Python 2 should use the prefix ``u``. """ def __new__(cls, index, system, pretty_str=None, latex_str=None): if pretty_str is None: pretty_str = "x{0}".format(index) if latex_str is None: latex_str = "x_{0}".format(index) pretty_str = str(pretty_str) latex_str = str(latex_str) # Verify arguments if index not in range(0, 3): raise ValueError("index must be 0, 1 or 2") if not isinstance(system, CoordSys3D): raise TypeError("system should be a CoordSys3D") name = system._vector_names[index] # Initialize an object obj = super(BaseVector, cls).__new__(cls, S(index), system) # Assign important attributes obj._base_instance = obj obj._components = {obj: S(1)} obj._measure_number = S(1) obj._name = system._name + '.' + name obj._pretty_form = u'' + pretty_str obj._latex_form = latex_str obj._system = system # The _id is used for printing purposes obj._id = (index, system) assumptions = {'commutative': True} obj._assumptions = StdFactKB(assumptions) # This attr is used for re-expression to one of the systems # involved in the definition of the Vector. Applies to # VectorMul and VectorAdd too. obj._sys = system return obj @property def system(self): return self._system def __str__(self, printer=None): return self._name @property def free_symbols(self): return {self} __repr__ = __str__ _sympystr = __str__ class VectorAdd(BasisDependentAdd, Vector): """ Class to denote sum of Vector instances. """ def __new__(cls, *args, **options): obj = BasisDependentAdd.__new__(cls, *args, **options) return obj def __str__(self, printer=None): ret_str = '' items = list(self.separate().items()) items.sort(key=lambda x: x[0].__str__()) for system, vect in items: base_vects = system.base_vectors() for x in base_vects: if x in vect.components: temp_vect = self.components[x] * x ret_str += temp_vect.__str__(printer) + " + " return ret_str[:-3] __repr__ = __str__ _sympystr = __str__ class VectorMul(BasisDependentMul, Vector): """ Class to denote products of scalars and BaseVectors. """ def __new__(cls, *args, **options): obj = BasisDependentMul.__new__(cls, *args, **options) return obj @property def base_vector(self): """ The BaseVector involved in the product. """ return self._base_instance @property def measure_number(self): """ The scalar expression involved in the definition of this VectorMul. """ return self._measure_number class VectorZero(BasisDependentZero, Vector): """ Class to denote a zero vector """ _op_priority = 12.1 _pretty_form = u'0' _latex_form = r'\mathbf{\hat{0}}' def __new__(cls): obj = BasisDependentZero.__new__(cls) return obj class Cross(Vector): """ Represents unevaluated Cross product. Examples ======== >>> from sympy.vector import CoordSys3D, Cross >>> R = CoordSys3D('R') >>> v1 = R.i + R.j + R.k >>> v2 = R.x * R.i + R.y * R.j + R.z * R.k >>> Cross(v1, v2) Cross(R.i + R.j + R.k, R.x*R.i + R.y*R.j + R.z*R.k) >>> Cross(v1, v2).doit() (-R.y + R.z)*R.i + (R.x - R.z)*R.j + (-R.x + R.y)*R.k """ def __new__(cls, expr1, expr2): expr1 = sympify(expr1) expr2 = sympify(expr2) if default_sort_key(expr1) > default_sort_key(expr2): return -Cross(expr2, expr1) obj = Expr.__new__(cls, expr1, expr2) obj._expr1 = expr1 obj._expr2 = expr2 return obj def doit(self, **kwargs): return cross(self._expr1, self._expr2) class Dot(Expr): """ Represents unevaluated Dot product. Examples ======== >>> from sympy.vector import CoordSys3D, Dot >>> from sympy import symbols >>> R = CoordSys3D('R') >>> a, b, c = symbols('a b c') >>> v1 = R.i + R.j + R.k >>> v2 = a * R.i + b * R.j + c * R.k >>> Dot(v1, v2) Dot(R.i + R.j + R.k, a*R.i + b*R.j + c*R.k) >>> Dot(v1, v2).doit() a + b + c """ def __new__(cls, expr1, expr2): expr1 = sympify(expr1) expr2 = sympify(expr2) expr1, expr2 = sorted([expr1, expr2], key=default_sort_key) obj = Expr.__new__(cls, expr1, expr2) obj._expr1 = expr1 obj._expr2 = expr2 return obj def doit(self, **kwargs): return dot(self._expr1, self._expr2) def cross(vect1, vect2): """ Returns cross product of two vectors. Examples ======== >>> from sympy.vector import CoordSys3D >>> from sympy.vector.vector import cross >>> R = CoordSys3D('R') >>> v1 = R.i + R.j + R.k >>> v2 = R.x * R.i + R.y * R.j + R.z * R.k >>> cross(v1, v2) (-R.y + R.z)*R.i + (R.x - R.z)*R.j + (-R.x + R.y)*R.k """ if isinstance(vect1, Add): return VectorAdd.fromiter(cross(i, vect2) for i in vect1.args) if isinstance(vect2, Add): return VectorAdd.fromiter(cross(vect1, i) for i in vect2.args) if isinstance(vect1, BaseVector) and isinstance(vect2, BaseVector): if vect1._sys == vect2._sys: n1 = vect1.args[0] n2 = vect2.args[0] if n1 == n2: return Vector.zero n3 = ({0,1,2}.difference({n1, n2})).pop() sign = 1 if ((n1 + 1) % 3 == n2) else -1 return sign*vect1._sys.base_vectors()[n3] try: from .functions import express return cross(express(vect1, vect2._sys), vect2) except: return Cross(vect1, vect2) if isinstance(vect1, VectorZero) or isinstance(vect2, VectorZero): return Vector.zero if isinstance(vect1, VectorMul): v1, m1 = next(iter(vect1.components.items())) return m1*cross(v1, vect2) if isinstance(vect2, VectorMul): v2, m2 = next(iter(vect2.components.items())) return m2*cross(vect1, v2) return Cross(vect1, vect2) def dot(vect1, vect2): """ Returns dot product of two vectors. Examples ======== >>> from sympy.vector import CoordSys3D >>> from sympy.vector.vector import dot >>> R = CoordSys3D('R') >>> v1 = R.i + R.j + R.k >>> v2 = R.x * R.i + R.y * R.j + R.z * R.k >>> dot(v1, v2) R.x + R.y + R.z """ if isinstance(vect1, Add): return Add.fromiter(dot(i, vect2) for i in vect1.args) if isinstance(vect2, Add): return Add.fromiter(dot(vect1, i) for i in vect2.args) if isinstance(vect1, BaseVector) and isinstance(vect2, BaseVector): if vect1._sys == vect2._sys: return S.One if vect1 == vect2 else S.Zero try: from .functions import express return dot(vect1, express(vect2, vect1._sys)) except: return Dot(vect1, vect2) if isinstance(vect1, VectorZero) or isinstance(vect2, VectorZero): return S.Zero if isinstance(vect1, VectorMul): v1, m1 = next(iter(vect1.components.items())) return m1*dot(v1, vect2) if isinstance(vect2, VectorMul): v2, m2 = next(iter(vect2.components.items())) return m2*dot(vect1, v2) return Dot(vect1, vect2) def _vect_div(one, other): """ Helper for division involving vectors. """ if isinstance(one, Vector) and isinstance(other, Vector): raise TypeError("Cannot divide two vectors") elif isinstance(one, Vector): if other == S.Zero: raise ValueError("Cannot divide a vector by zero") return VectorMul(one, Pow(other, S.NegativeOne)) else: raise TypeError("Invalid division involving a vector") Vector._expr_type = Vector Vector._mul_func = VectorMul Vector._add_func = VectorAdd Vector._zero_func = VectorZero Vector._base_func = BaseVector Vector._div_helper = _vect_div Vector.zero = VectorZero()