vector.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. from sympy.core.backend import (S, sympify, expand, sqrt, Add, zeros, acos,
  2. ImmutableMatrix as Matrix, _simplify_matrix)
  3. from sympy.simplify.trigsimp import trigsimp
  4. from sympy.printing.defaults import Printable
  5. from sympy.utilities.misc import filldedent
  6. from sympy.core.evalf import EvalfMixin
  7. from mpmath.libmp.libmpf import prec_to_dps
  8. __all__ = ['Vector']
  9. class Vector(Printable, EvalfMixin):
  10. """The class used to define vectors.
  11. It along with ReferenceFrame are the building blocks of describing a
  12. classical mechanics system in PyDy and sympy.physics.vector.
  13. Attributes
  14. ==========
  15. simp : Boolean
  16. Let certain methods use trigsimp on their outputs
  17. """
  18. simp = False
  19. is_number = False
  20. def __init__(self, inlist):
  21. """This is the constructor for the Vector class. You should not be
  22. calling this, it should only be used by other functions. You should be
  23. treating Vectors like you would with if you were doing the math by
  24. hand, and getting the first 3 from the standard basis vectors from a
  25. ReferenceFrame.
  26. The only exception is to create a zero vector:
  27. zv = Vector(0)
  28. """
  29. self.args = []
  30. if inlist == 0:
  31. inlist = []
  32. if isinstance(inlist, dict):
  33. d = inlist
  34. else:
  35. d = {}
  36. for inp in inlist:
  37. if inp[1] in d:
  38. d[inp[1]] += inp[0]
  39. else:
  40. d[inp[1]] = inp[0]
  41. for k, v in d.items():
  42. if v != Matrix([0, 0, 0]):
  43. self.args.append((v, k))
  44. @property
  45. def func(self):
  46. """Returns the class Vector. """
  47. return Vector
  48. def __hash__(self):
  49. return hash(tuple(self.args))
  50. def __add__(self, other):
  51. """The add operator for Vector. """
  52. if other == 0:
  53. return self
  54. other = _check_vector(other)
  55. return Vector(self.args + other.args)
  56. def __and__(self, other):
  57. """Dot product of two vectors.
  58. Returns a scalar, the dot product of the two Vectors
  59. Parameters
  60. ==========
  61. other : Vector
  62. The Vector which we are dotting with
  63. Examples
  64. ========
  65. >>> from sympy.physics.vector import ReferenceFrame, dot
  66. >>> from sympy import symbols
  67. >>> q1 = symbols('q1')
  68. >>> N = ReferenceFrame('N')
  69. >>> dot(N.x, N.x)
  70. 1
  71. >>> dot(N.x, N.y)
  72. 0
  73. >>> A = N.orientnew('A', 'Axis', [q1, N.x])
  74. >>> dot(N.y, A.y)
  75. cos(q1)
  76. """
  77. from sympy.physics.vector.dyadic import Dyadic
  78. if isinstance(other, Dyadic):
  79. return NotImplemented
  80. other = _check_vector(other)
  81. out = S.Zero
  82. for i, v1 in enumerate(self.args):
  83. for j, v2 in enumerate(other.args):
  84. out += ((v2[0].T)
  85. * (v2[1].dcm(v1[1]))
  86. * (v1[0]))[0]
  87. if Vector.simp:
  88. return trigsimp(out, recursive=True)
  89. else:
  90. return out
  91. def __truediv__(self, other):
  92. """This uses mul and inputs self and 1 divided by other. """
  93. return self.__mul__(S.One / other)
  94. def __eq__(self, other):
  95. """Tests for equality.
  96. It is very import to note that this is only as good as the SymPy
  97. equality test; False does not always mean they are not equivalent
  98. Vectors.
  99. If other is 0, and self is empty, returns True.
  100. If other is 0 and self is not empty, returns False.
  101. If none of the above, only accepts other as a Vector.
  102. """
  103. if other == 0:
  104. other = Vector(0)
  105. try:
  106. other = _check_vector(other)
  107. except TypeError:
  108. return False
  109. if (self.args == []) and (other.args == []):
  110. return True
  111. elif (self.args == []) or (other.args == []):
  112. return False
  113. frame = self.args[0][1]
  114. for v in frame:
  115. if expand((self - other) & v) != 0:
  116. return False
  117. return True
  118. def __mul__(self, other):
  119. """Multiplies the Vector by a sympifyable expression.
  120. Parameters
  121. ==========
  122. other : Sympifyable
  123. The scalar to multiply this Vector with
  124. Examples
  125. ========
  126. >>> from sympy.physics.vector import ReferenceFrame
  127. >>> from sympy import Symbol
  128. >>> N = ReferenceFrame('N')
  129. >>> b = Symbol('b')
  130. >>> V = 10 * b * N.x
  131. >>> print(V)
  132. 10*b*N.x
  133. """
  134. newlist = list(self.args)
  135. other = sympify(other)
  136. for i, v in enumerate(newlist):
  137. newlist[i] = (other * newlist[i][0], newlist[i][1])
  138. return Vector(newlist)
  139. def __neg__(self):
  140. return self * -1
  141. def __or__(self, other):
  142. """Outer product between two Vectors.
  143. A rank increasing operation, which returns a Dyadic from two Vectors
  144. Parameters
  145. ==========
  146. other : Vector
  147. The Vector to take the outer product with
  148. Examples
  149. ========
  150. >>> from sympy.physics.vector import ReferenceFrame, outer
  151. >>> N = ReferenceFrame('N')
  152. >>> outer(N.x, N.x)
  153. (N.x|N.x)
  154. """
  155. from sympy.physics.vector.dyadic import Dyadic
  156. other = _check_vector(other)
  157. ol = Dyadic(0)
  158. for i, v in enumerate(self.args):
  159. for i2, v2 in enumerate(other.args):
  160. # it looks this way because if we are in the same frame and
  161. # use the enumerate function on the same frame in a nested
  162. # fashion, then bad things happen
  163. ol += Dyadic([(v[0][0] * v2[0][0], v[1].x, v2[1].x)])
  164. ol += Dyadic([(v[0][0] * v2[0][1], v[1].x, v2[1].y)])
  165. ol += Dyadic([(v[0][0] * v2[0][2], v[1].x, v2[1].z)])
  166. ol += Dyadic([(v[0][1] * v2[0][0], v[1].y, v2[1].x)])
  167. ol += Dyadic([(v[0][1] * v2[0][1], v[1].y, v2[1].y)])
  168. ol += Dyadic([(v[0][1] * v2[0][2], v[1].y, v2[1].z)])
  169. ol += Dyadic([(v[0][2] * v2[0][0], v[1].z, v2[1].x)])
  170. ol += Dyadic([(v[0][2] * v2[0][1], v[1].z, v2[1].y)])
  171. ol += Dyadic([(v[0][2] * v2[0][2], v[1].z, v2[1].z)])
  172. return ol
  173. def _latex(self, printer):
  174. """Latex Printing method. """
  175. ar = self.args # just to shorten things
  176. if len(ar) == 0:
  177. return str(0)
  178. ol = [] # output list, to be concatenated to a string
  179. for i, v in enumerate(ar):
  180. for j in 0, 1, 2:
  181. # if the coef of the basis vector is 1, we skip the 1
  182. if ar[i][0][j] == 1:
  183. ol.append(' + ' + ar[i][1].latex_vecs[j])
  184. # if the coef of the basis vector is -1, we skip the 1
  185. elif ar[i][0][j] == -1:
  186. ol.append(' - ' + ar[i][1].latex_vecs[j])
  187. elif ar[i][0][j] != 0:
  188. # If the coefficient of the basis vector is not 1 or -1;
  189. # also, we might wrap it in parentheses, for readability.
  190. arg_str = printer._print(ar[i][0][j])
  191. if isinstance(ar[i][0][j], Add):
  192. arg_str = "(%s)" % arg_str
  193. if arg_str[0] == '-':
  194. arg_str = arg_str[1:]
  195. str_start = ' - '
  196. else:
  197. str_start = ' + '
  198. ol.append(str_start + arg_str + ar[i][1].latex_vecs[j])
  199. outstr = ''.join(ol)
  200. if outstr.startswith(' + '):
  201. outstr = outstr[3:]
  202. elif outstr.startswith(' '):
  203. outstr = outstr[1:]
  204. return outstr
  205. def _pretty(self, printer):
  206. """Pretty Printing method. """
  207. from sympy.printing.pretty.stringpict import prettyForm
  208. e = self
  209. class Fake:
  210. def render(self, *args, **kwargs):
  211. ar = e.args # just to shorten things
  212. if len(ar) == 0:
  213. return str(0)
  214. pforms = [] # output list, to be concatenated to a string
  215. for i, v in enumerate(ar):
  216. for j in 0, 1, 2:
  217. # if the coef of the basis vector is 1, we skip the 1
  218. if ar[i][0][j] == 1:
  219. pform = printer._print(ar[i][1].pretty_vecs[j])
  220. # if the coef of the basis vector is -1, we skip the 1
  221. elif ar[i][0][j] == -1:
  222. pform = printer._print(ar[i][1].pretty_vecs[j])
  223. pform = prettyForm(*pform.left(" - "))
  224. bin = prettyForm.NEG
  225. pform = prettyForm(binding=bin, *pform)
  226. elif ar[i][0][j] != 0:
  227. # If the basis vector coeff is not 1 or -1,
  228. # we might wrap it in parentheses, for readability.
  229. pform = printer._print(ar[i][0][j])
  230. if isinstance(ar[i][0][j], Add):
  231. tmp = pform.parens()
  232. pform = prettyForm(tmp[0], tmp[1])
  233. pform = prettyForm(*pform.right(
  234. " ", ar[i][1].pretty_vecs[j]))
  235. else:
  236. continue
  237. pforms.append(pform)
  238. pform = prettyForm.__add__(*pforms)
  239. kwargs["wrap_line"] = kwargs.get("wrap_line")
  240. kwargs["num_columns"] = kwargs.get("num_columns")
  241. out_str = pform.render(*args, **kwargs)
  242. mlines = [line.rstrip() for line in out_str.split("\n")]
  243. return "\n".join(mlines)
  244. return Fake()
  245. def __ror__(self, other):
  246. """Outer product between two Vectors.
  247. A rank increasing operation, which returns a Dyadic from two Vectors
  248. Parameters
  249. ==========
  250. other : Vector
  251. The Vector to take the outer product with
  252. Examples
  253. ========
  254. >>> from sympy.physics.vector import ReferenceFrame, outer
  255. >>> N = ReferenceFrame('N')
  256. >>> outer(N.x, N.x)
  257. (N.x|N.x)
  258. """
  259. from sympy.physics.vector.dyadic import Dyadic
  260. other = _check_vector(other)
  261. ol = Dyadic(0)
  262. for i, v in enumerate(other.args):
  263. for i2, v2 in enumerate(self.args):
  264. # it looks this way because if we are in the same frame and
  265. # use the enumerate function on the same frame in a nested
  266. # fashion, then bad things happen
  267. ol += Dyadic([(v[0][0] * v2[0][0], v[1].x, v2[1].x)])
  268. ol += Dyadic([(v[0][0] * v2[0][1], v[1].x, v2[1].y)])
  269. ol += Dyadic([(v[0][0] * v2[0][2], v[1].x, v2[1].z)])
  270. ol += Dyadic([(v[0][1] * v2[0][0], v[1].y, v2[1].x)])
  271. ol += Dyadic([(v[0][1] * v2[0][1], v[1].y, v2[1].y)])
  272. ol += Dyadic([(v[0][1] * v2[0][2], v[1].y, v2[1].z)])
  273. ol += Dyadic([(v[0][2] * v2[0][0], v[1].z, v2[1].x)])
  274. ol += Dyadic([(v[0][2] * v2[0][1], v[1].z, v2[1].y)])
  275. ol += Dyadic([(v[0][2] * v2[0][2], v[1].z, v2[1].z)])
  276. return ol
  277. def __rsub__(self, other):
  278. return (-1 * self) + other
  279. def _sympystr(self, printer, order=True):
  280. """Printing method. """
  281. if not order or len(self.args) == 1:
  282. ar = list(self.args)
  283. elif len(self.args) == 0:
  284. return printer._print(0)
  285. else:
  286. d = {v[1]: v[0] for v in self.args}
  287. keys = sorted(d.keys(), key=lambda x: x.index)
  288. ar = []
  289. for key in keys:
  290. ar.append((d[key], key))
  291. ol = [] # output list, to be concatenated to a string
  292. for i, v in enumerate(ar):
  293. for j in 0, 1, 2:
  294. # if the coef of the basis vector is 1, we skip the 1
  295. if ar[i][0][j] == 1:
  296. ol.append(' + ' + ar[i][1].str_vecs[j])
  297. # if the coef of the basis vector is -1, we skip the 1
  298. elif ar[i][0][j] == -1:
  299. ol.append(' - ' + ar[i][1].str_vecs[j])
  300. elif ar[i][0][j] != 0:
  301. # If the coefficient of the basis vector is not 1 or -1;
  302. # also, we might wrap it in parentheses, for readability.
  303. arg_str = printer._print(ar[i][0][j])
  304. if isinstance(ar[i][0][j], Add):
  305. arg_str = "(%s)" % arg_str
  306. if arg_str[0] == '-':
  307. arg_str = arg_str[1:]
  308. str_start = ' - '
  309. else:
  310. str_start = ' + '
  311. ol.append(str_start + arg_str + '*' + ar[i][1].str_vecs[j])
  312. outstr = ''.join(ol)
  313. if outstr.startswith(' + '):
  314. outstr = outstr[3:]
  315. elif outstr.startswith(' '):
  316. outstr = outstr[1:]
  317. return outstr
  318. def __sub__(self, other):
  319. """The subtraction operator. """
  320. return self.__add__(other * -1)
  321. def __xor__(self, other):
  322. """The cross product operator for two Vectors.
  323. Returns a Vector, expressed in the same ReferenceFrames as self.
  324. Parameters
  325. ==========
  326. other : Vector
  327. The Vector which we are crossing with
  328. Examples
  329. ========
  330. >>> from sympy import symbols
  331. >>> from sympy.physics.vector import ReferenceFrame, cross
  332. >>> q1 = symbols('q1')
  333. >>> N = ReferenceFrame('N')
  334. >>> cross(N.x, N.y)
  335. N.z
  336. >>> A = ReferenceFrame('A')
  337. >>> A.orient_axis(N, q1, N.x)
  338. >>> cross(A.x, N.y)
  339. N.z
  340. >>> cross(N.y, A.x)
  341. - sin(q1)*A.y - cos(q1)*A.z
  342. """
  343. from sympy.physics.vector.dyadic import Dyadic
  344. if isinstance(other, Dyadic):
  345. return NotImplemented
  346. other = _check_vector(other)
  347. if other.args == []:
  348. return Vector(0)
  349. def _det(mat):
  350. """This is needed as a little method for to find the determinant
  351. of a list in python; needs to work for a 3x3 list.
  352. SymPy's Matrix will not take in Vector, so need a custom function.
  353. You should not be calling this.
  354. """
  355. return (mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1])
  356. + mat[0][1] * (mat[1][2] * mat[2][0] - mat[1][0] *
  357. mat[2][2]) + mat[0][2] * (mat[1][0] * mat[2][1] -
  358. mat[1][1] * mat[2][0]))
  359. outlist = []
  360. ar = other.args # For brevity
  361. for i, v in enumerate(ar):
  362. tempx = v[1].x
  363. tempy = v[1].y
  364. tempz = v[1].z
  365. tempm = ([[tempx, tempy, tempz],
  366. [self & tempx, self & tempy, self & tempz],
  367. [Vector([ar[i]]) & tempx, Vector([ar[i]]) & tempy,
  368. Vector([ar[i]]) & tempz]])
  369. outlist += _det(tempm).args
  370. return Vector(outlist)
  371. __radd__ = __add__
  372. __rand__ = __and__
  373. __rmul__ = __mul__
  374. def separate(self):
  375. """
  376. The constituents of this vector in different reference frames,
  377. as per its definition.
  378. Returns a dict mapping each ReferenceFrame to the corresponding
  379. constituent Vector.
  380. Examples
  381. ========
  382. >>> from sympy.physics.vector import ReferenceFrame
  383. >>> R1 = ReferenceFrame('R1')
  384. >>> R2 = ReferenceFrame('R2')
  385. >>> v = R1.x + R2.x
  386. >>> v.separate() == {R1: R1.x, R2: R2.x}
  387. True
  388. """
  389. components = {}
  390. for x in self.args:
  391. components[x[1]] = Vector([x])
  392. return components
  393. def dot(self, other):
  394. return self & other
  395. dot.__doc__ = __and__.__doc__
  396. def cross(self, other):
  397. return self ^ other
  398. cross.__doc__ = __xor__.__doc__
  399. def outer(self, other):
  400. return self | other
  401. outer.__doc__ = __or__.__doc__
  402. def diff(self, var, frame, var_in_dcm=True):
  403. """Returns the partial derivative of the vector with respect to a
  404. variable in the provided reference frame.
  405. Parameters
  406. ==========
  407. var : Symbol
  408. What the partial derivative is taken with respect to.
  409. frame : ReferenceFrame
  410. The reference frame that the partial derivative is taken in.
  411. var_in_dcm : boolean
  412. If true, the differentiation algorithm assumes that the variable
  413. may be present in any of the direction cosine matrices that relate
  414. the frame to the frames of any component of the vector. But if it
  415. is known that the variable is not present in the direction cosine
  416. matrices, false can be set to skip full reexpression in the desired
  417. frame.
  418. Examples
  419. ========
  420. >>> from sympy import Symbol
  421. >>> from sympy.physics.vector import dynamicsymbols, ReferenceFrame
  422. >>> from sympy.physics.vector import Vector
  423. >>> from sympy.physics.vector import init_vprinting
  424. >>> init_vprinting(pretty_print=False)
  425. >>> Vector.simp = True
  426. >>> t = Symbol('t')
  427. >>> q1 = dynamicsymbols('q1')
  428. >>> N = ReferenceFrame('N')
  429. >>> A = N.orientnew('A', 'Axis', [q1, N.y])
  430. >>> A.x.diff(t, N)
  431. - sin(q1)*q1'*N.x - cos(q1)*q1'*N.z
  432. >>> A.x.diff(t, N).express(A)
  433. - q1'*A.z
  434. >>> B = ReferenceFrame('B')
  435. >>> u1, u2 = dynamicsymbols('u1, u2')
  436. >>> v = u1 * A.x + u2 * B.y
  437. >>> v.diff(u2, N, var_in_dcm=False)
  438. B.y
  439. """
  440. from sympy.physics.vector.frame import _check_frame
  441. _check_frame(frame)
  442. var = sympify(var)
  443. inlist = []
  444. for vector_component in self.args:
  445. measure_number = vector_component[0]
  446. component_frame = vector_component[1]
  447. if component_frame == frame:
  448. inlist += [(measure_number.diff(var), frame)]
  449. else:
  450. # If the direction cosine matrix relating the component frame
  451. # with the derivative frame does not contain the variable.
  452. if not var_in_dcm or (frame.dcm(component_frame).diff(var) ==
  453. zeros(3, 3)):
  454. inlist += [(measure_number.diff(var), component_frame)]
  455. else: # else express in the frame
  456. reexp_vec_comp = Vector([vector_component]).express(frame)
  457. deriv = reexp_vec_comp.args[0][0].diff(var)
  458. inlist += Vector([(deriv, frame)]).args
  459. return Vector(inlist)
  460. def express(self, otherframe, variables=False):
  461. """
  462. Returns a Vector equivalent to this one, expressed in otherframe.
  463. Uses the global express method.
  464. Parameters
  465. ==========
  466. otherframe : ReferenceFrame
  467. The frame for this Vector to be described in
  468. variables : boolean
  469. If True, the coordinate symbols(if present) in this Vector
  470. are re-expressed in terms otherframe
  471. Examples
  472. ========
  473. >>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
  474. >>> from sympy.physics.vector import init_vprinting
  475. >>> init_vprinting(pretty_print=False)
  476. >>> q1 = dynamicsymbols('q1')
  477. >>> N = ReferenceFrame('N')
  478. >>> A = N.orientnew('A', 'Axis', [q1, N.y])
  479. >>> A.x.express(N)
  480. cos(q1)*N.x - sin(q1)*N.z
  481. """
  482. from sympy.physics.vector import express
  483. return express(self, otherframe, variables=variables)
  484. def to_matrix(self, reference_frame):
  485. """Returns the matrix form of the vector with respect to the given
  486. frame.
  487. Parameters
  488. ----------
  489. reference_frame : ReferenceFrame
  490. The reference frame that the rows of the matrix correspond to.
  491. Returns
  492. -------
  493. matrix : ImmutableMatrix, shape(3,1)
  494. The matrix that gives the 1D vector.
  495. Examples
  496. ========
  497. >>> from sympy import symbols
  498. >>> from sympy.physics.vector import ReferenceFrame
  499. >>> a, b, c = symbols('a, b, c')
  500. >>> N = ReferenceFrame('N')
  501. >>> vector = a * N.x + b * N.y + c * N.z
  502. >>> vector.to_matrix(N)
  503. Matrix([
  504. [a],
  505. [b],
  506. [c]])
  507. >>> beta = symbols('beta')
  508. >>> A = N.orientnew('A', 'Axis', (beta, N.x))
  509. >>> vector.to_matrix(A)
  510. Matrix([
  511. [ a],
  512. [ b*cos(beta) + c*sin(beta)],
  513. [-b*sin(beta) + c*cos(beta)]])
  514. """
  515. return Matrix([self.dot(unit_vec) for unit_vec in
  516. reference_frame]).reshape(3, 1)
  517. def doit(self, **hints):
  518. """Calls .doit() on each term in the Vector"""
  519. d = {}
  520. for v in self.args:
  521. d[v[1]] = v[0].applyfunc(lambda x: x.doit(**hints))
  522. return Vector(d)
  523. def dt(self, otherframe):
  524. """
  525. Returns a Vector which is the time derivative of
  526. the self Vector, taken in frame otherframe.
  527. Calls the global time_derivative method
  528. Parameters
  529. ==========
  530. otherframe : ReferenceFrame
  531. The frame to calculate the time derivative in
  532. """
  533. from sympy.physics.vector import time_derivative
  534. return time_derivative(self, otherframe)
  535. def simplify(self):
  536. """Returns a simplified Vector."""
  537. d = {}
  538. for v in self.args:
  539. d[v[1]] = _simplify_matrix(v[0])
  540. return Vector(d)
  541. def subs(self, *args, **kwargs):
  542. """Substitution on the Vector.
  543. Examples
  544. ========
  545. >>> from sympy.physics.vector import ReferenceFrame
  546. >>> from sympy import Symbol
  547. >>> N = ReferenceFrame('N')
  548. >>> s = Symbol('s')
  549. >>> a = N.x * s
  550. >>> a.subs({s: 2})
  551. 2*N.x
  552. """
  553. d = {}
  554. for v in self.args:
  555. d[v[1]] = v[0].subs(*args, **kwargs)
  556. return Vector(d)
  557. def magnitude(self):
  558. """Returns the magnitude (Euclidean norm) of self.
  559. Warnings
  560. ========
  561. Python ignores the leading negative sign so that might
  562. give wrong results.
  563. ``-A.x.magnitude()`` would be treated as ``-(A.x.magnitude())``,
  564. instead of ``(-A.x).magnitude()``.
  565. """
  566. return sqrt(self & self)
  567. def normalize(self):
  568. """Returns a Vector of magnitude 1, codirectional with self."""
  569. return Vector(self.args + []) / self.magnitude()
  570. def applyfunc(self, f):
  571. """Apply a function to each component of a vector."""
  572. if not callable(f):
  573. raise TypeError("`f` must be callable.")
  574. d = {}
  575. for v in self.args:
  576. d[v[1]] = v[0].applyfunc(f)
  577. return Vector(d)
  578. def angle_between(self, vec):
  579. """
  580. Returns the smallest angle between Vector 'vec' and self.
  581. Parameter
  582. =========
  583. vec : Vector
  584. The Vector between which angle is needed.
  585. Examples
  586. ========
  587. >>> from sympy.physics.vector import ReferenceFrame
  588. >>> A = ReferenceFrame("A")
  589. >>> v1 = A.x
  590. >>> v2 = A.y
  591. >>> v1.angle_between(v2)
  592. pi/2
  593. >>> v3 = A.x + A.y + A.z
  594. >>> v1.angle_between(v3)
  595. acos(sqrt(3)/3)
  596. Warnings
  597. ========
  598. Python ignores the leading negative sign so that might give wrong
  599. results. ``-A.x.angle_between()`` would be treated as
  600. ``-(A.x.angle_between())``, instead of ``(-A.x).angle_between()``.
  601. """
  602. vec1 = self.normalize()
  603. vec2 = vec.normalize()
  604. angle = acos(vec1.dot(vec2))
  605. return angle
  606. def free_symbols(self, reference_frame):
  607. """Returns the free symbols in the measure numbers of the vector
  608. expressed in the given reference frame.
  609. Parameters
  610. ==========
  611. reference_frame : ReferenceFrame
  612. The frame with respect to which the free symbols of the given
  613. vector is to be determined.
  614. Returns
  615. =======
  616. set of Symbol
  617. set of symbols present in the measure numbers of
  618. ``reference_frame``.
  619. """
  620. return self.to_matrix(reference_frame).free_symbols
  621. def free_dynamicsymbols(self, reference_frame):
  622. """Returns the free dynamic symbols (functions of time ``t``) in the
  623. measure numbers of the vector expressed in the given reference frame.
  624. Parameters
  625. ==========
  626. reference_frame : ReferenceFrame
  627. The frame with respect to which the free dynamic symbols of the
  628. given vector is to be determined.
  629. Returns
  630. =======
  631. set
  632. Set of functions of time ``t``, e.g.
  633. ``Function('f')(me.dynamicsymbols._t)``.
  634. """
  635. # TODO : Circular dependency if imported at top. Should move
  636. # find_dynamicsymbols into physics.vector.functions.
  637. from sympy.physics.mechanics.functions import find_dynamicsymbols
  638. return find_dynamicsymbols(self, reference_frame=reference_frame)
  639. def _eval_evalf(self, prec):
  640. if not self.args:
  641. return self
  642. new_args = []
  643. dps = prec_to_dps(prec)
  644. for mat, frame in self.args:
  645. new_args.append([mat.evalf(n=dps), frame])
  646. return Vector(new_args)
  647. def xreplace(self, rule):
  648. """Replace occurrences of objects within the measure numbers of the
  649. vector.
  650. Parameters
  651. ==========
  652. rule : dict-like
  653. Expresses a replacement rule.
  654. Returns
  655. =======
  656. Vector
  657. Result of the replacement.
  658. Examples
  659. ========
  660. >>> from sympy import symbols, pi
  661. >>> from sympy.physics.vector import ReferenceFrame
  662. >>> A = ReferenceFrame('A')
  663. >>> x, y, z = symbols('x y z')
  664. >>> ((1 + x*y) * A.x).xreplace({x: pi})
  665. (pi*y + 1)*A.x
  666. >>> ((1 + x*y) * A.x).xreplace({x: pi, y: 2})
  667. (1 + 2*pi)*A.x
  668. Replacements occur only if an entire node in the expression tree is
  669. matched:
  670. >>> ((x*y + z) * A.x).xreplace({x*y: pi})
  671. (z + pi)*A.x
  672. >>> ((x*y*z) * A.x).xreplace({x*y: pi})
  673. x*y*z*A.x
  674. """
  675. new_args = []
  676. for mat, frame in self.args:
  677. mat = mat.xreplace(rule)
  678. new_args.append([mat, frame])
  679. return Vector(new_args)
  680. class VectorTypeError(TypeError):
  681. def __init__(self, other, want):
  682. msg = filldedent("Expected an instance of %s, but received object "
  683. "'%s' of %s." % (type(want), other, type(other)))
  684. super().__init__(msg)
  685. def _check_vector(other):
  686. if not isinstance(other, Vector):
  687. raise TypeError('A Vector must be supplied')
  688. return other