# 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
BasisDependentMul, BasisDependentZero)

[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__
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 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()