123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- from __future__ import annotations
- from itertools import product
- from sympy.core.add import Add
- from sympy.core.assumptions import StdFactKB
- from sympy.core.expr import AtomicExpr, Expr
- from sympy.core.power import Pow
- from sympy.core.singleton import S
- from sympy.core.sorting import default_sort_key
- from sympy.core.sympify import sympify
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
- from sympy.vector.basisdependent import (BasisDependentZero,
- BasisDependent, BasisDependentMul, BasisDependentAdd)
- from sympy.vector.coordsysrect import CoordSys3D
- from sympy.vector.dyadic import Dyadic, BaseDyadic, DyadicAdd
- 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_scalar = False
- is_Vector = True
- _op_priority = 12.0
- _expr_type: type[Vector]
- _mul_func: type[Vector]
- _add_func: type[Vector]
- _zero_func: type[Vector]
- _base_func: type[Vector]
- zero: VectorZero
- @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
- def magnitude(self):
- """
- Returns the magnitude of this vector.
- """
- return sqrt(self & self)
- def normalize(self):
- """
- Returns the normalized version of this vector.
- """
- return self / self.magnitude()
- 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, (Del, Vector)):
- 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__
- 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__
- 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 = [(v1 * v2) * BaseDyadic(k1, k2) for (k1, v1), (k2, v2)
- in product(self.components.items(), other.components.items())]
- return DyadicAdd(*args)
- def projection(self, other, scalar=False):
- """
- Returns the vector or scalar projection of the 'other' on 'self'.
- Examples
- ========
- >>> from sympy.vector.coordsysrect import CoordSys3D
- >>> 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_systems
- if isinstance(self, VectorZero):
- return (S.Zero, S.Zero, S.Zero)
- base_vec = next(iter(_get_coord_systems(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__
- 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()])
- 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
- def _div_helper(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")
- class BaseVector(Vector, AtomicExpr):
- """
- Class to denote a base vector.
- """
- def __new__(cls, index, system, pretty_str=None, latex_str=None):
- if pretty_str is None:
- pretty_str = "x{}".format(index)
- if latex_str is None:
- latex_str = "x_{}".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().__new__(cls, S(index), system)
- # Assign important attributes
- obj._base_instance = obj
- obj._components = {obj: S.One}
- obj._measure_number = S.One
- obj._name = system._name + '.' + name
- obj._pretty_form = '' + 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 _sympystr(self, printer):
- return self._name
- def _sympyrepr(self, printer):
- index, system = self._id
- return printer._print(system) + '.' + system._vector_names[index]
- @property
- def free_symbols(self):
- return {self}
- 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 _sympystr(self, printer):
- 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 += printer._print(temp_vect) + " + "
- return ret_str[:-3]
- 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 = '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, **hints):
- 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, **hints):
- 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]
- from .functions import express
- try:
- v = express(vect1, vect2._sys)
- except ValueError:
- return Cross(vect1, vect2)
- else:
- return cross(v, 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
- from .functions import express
- try:
- v = express(vect2, vect1._sys)
- except ValueError:
- return Dot(vect1, vect2)
- else:
- return dot(vect1, v)
- 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)
- Vector._expr_type = Vector
- Vector._mul_func = VectorMul
- Vector._add_func = VectorAdd
- Vector._zero_func = VectorZero
- Vector._base_func = BaseVector
- Vector.zero = VectorZero()
|