m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

595 lines
19 KiB

7 months ago
  1. from sympy.core.backend import sympify, Add, ImmutableMatrix as Matrix
  2. from sympy.core.evalf import EvalfMixin
  3. from sympy.printing.defaults import Printable
  4. from mpmath.libmp.libmpf import prec_to_dps
  5. __all__ = ['Dyadic']
  6. class Dyadic(Printable, EvalfMixin):
  7. """A Dyadic object.
  8. See:
  9. https://en.wikipedia.org/wiki/Dyadic_tensor
  10. Kane, T., Levinson, D. Dynamics Theory and Applications. 1985 McGraw-Hill
  11. A more powerful way to represent a rigid body's inertia. While it is more
  12. complex, by choosing Dyadic components to be in body fixed basis vectors,
  13. the resulting matrix is equivalent to the inertia tensor.
  14. """
  15. is_number = False
  16. def __init__(self, inlist):
  17. """
  18. Just like Vector's init, you shouldn't call this unless creating a
  19. zero dyadic.
  20. zd = Dyadic(0)
  21. Stores a Dyadic as a list of lists; the inner list has the measure
  22. number and the two unit vectors; the outerlist holds each unique
  23. unit vector pair.
  24. """
  25. self.args = []
  26. if inlist == 0:
  27. inlist = []
  28. while len(inlist) != 0:
  29. added = 0
  30. for i, v in enumerate(self.args):
  31. if ((str(inlist[0][1]) == str(self.args[i][1])) and
  32. (str(inlist[0][2]) == str(self.args[i][2]))):
  33. self.args[i] = (self.args[i][0] + inlist[0][0],
  34. inlist[0][1], inlist[0][2])
  35. inlist.remove(inlist[0])
  36. added = 1
  37. break
  38. if added != 1:
  39. self.args.append(inlist[0])
  40. inlist.remove(inlist[0])
  41. i = 0
  42. # This code is to remove empty parts from the list
  43. while i < len(self.args):
  44. if ((self.args[i][0] == 0) | (self.args[i][1] == 0) |
  45. (self.args[i][2] == 0)):
  46. self.args.remove(self.args[i])
  47. i -= 1
  48. i += 1
  49. @property
  50. def func(self):
  51. """Returns the class Dyadic. """
  52. return Dyadic
  53. def __add__(self, other):
  54. """The add operator for Dyadic. """
  55. other = _check_dyadic(other)
  56. return Dyadic(self.args + other.args)
  57. def __and__(self, other):
  58. """The inner product operator for a Dyadic and a Dyadic or Vector.
  59. Parameters
  60. ==========
  61. other : Dyadic or Vector
  62. The other Dyadic or Vector to take the inner product with
  63. Examples
  64. ========
  65. >>> from sympy.physics.vector import ReferenceFrame, outer
  66. >>> N = ReferenceFrame('N')
  67. >>> D1 = outer(N.x, N.y)
  68. >>> D2 = outer(N.y, N.y)
  69. >>> D1.dot(D2)
  70. (N.x|N.y)
  71. >>> D1.dot(N.y)
  72. N.x
  73. """
  74. from sympy.physics.vector.vector import Vector, _check_vector
  75. if isinstance(other, Dyadic):
  76. other = _check_dyadic(other)
  77. ol = Dyadic(0)
  78. for i, v in enumerate(self.args):
  79. for i2, v2 in enumerate(other.args):
  80. ol += v[0] * v2[0] * (v[2] & v2[1]) * (v[1] | v2[2])
  81. else:
  82. other = _check_vector(other)
  83. ol = Vector(0)
  84. for i, v in enumerate(self.args):
  85. ol += v[0] * v[1] * (v[2] & other)
  86. return ol
  87. def __truediv__(self, other):
  88. """Divides the Dyadic by a sympifyable expression. """
  89. return self.__mul__(1 / other)
  90. def __eq__(self, other):
  91. """Tests for equality.
  92. Is currently weak; needs stronger comparison testing
  93. """
  94. if other == 0:
  95. other = Dyadic(0)
  96. other = _check_dyadic(other)
  97. if (self.args == []) and (other.args == []):
  98. return True
  99. elif (self.args == []) or (other.args == []):
  100. return False
  101. return set(self.args) == set(other.args)
  102. def __mul__(self, other):
  103. """Multiplies the Dyadic by a sympifyable expression.
  104. Parameters
  105. ==========
  106. other : Sympafiable
  107. The scalar to multiply this Dyadic with
  108. Examples
  109. ========
  110. >>> from sympy.physics.vector import ReferenceFrame, outer
  111. >>> N = ReferenceFrame('N')
  112. >>> d = outer(N.x, N.x)
  113. >>> 5 * d
  114. 5*(N.x|N.x)
  115. """
  116. newlist = [v for v in self.args]
  117. for i, v in enumerate(newlist):
  118. newlist[i] = (sympify(other) * newlist[i][0], newlist[i][1],
  119. newlist[i][2])
  120. return Dyadic(newlist)
  121. def __ne__(self, other):
  122. return not self == other
  123. def __neg__(self):
  124. return self * -1
  125. def _latex(self, printer):
  126. ar = self.args # just to shorten things
  127. if len(ar) == 0:
  128. return str(0)
  129. ol = [] # output list, to be concatenated to a string
  130. for i, v in enumerate(ar):
  131. # if the coef of the dyadic is 1, we skip the 1
  132. if ar[i][0] == 1:
  133. ol.append(' + ' + printer._print(ar[i][1]) + r"\otimes " +
  134. printer._print(ar[i][2]))
  135. # if the coef of the dyadic is -1, we skip the 1
  136. elif ar[i][0] == -1:
  137. ol.append(' - ' +
  138. printer._print(ar[i][1]) +
  139. r"\otimes " +
  140. printer._print(ar[i][2]))
  141. # If the coefficient of the dyadic is not 1 or -1,
  142. # we might wrap it in parentheses, for readability.
  143. elif ar[i][0] != 0:
  144. arg_str = printer._print(ar[i][0])
  145. if isinstance(ar[i][0], Add):
  146. arg_str = '(%s)' % arg_str
  147. if arg_str.startswith('-'):
  148. arg_str = arg_str[1:]
  149. str_start = ' - '
  150. else:
  151. str_start = ' + '
  152. ol.append(str_start + arg_str + printer._print(ar[i][1]) +
  153. r"\otimes " + printer._print(ar[i][2]))
  154. outstr = ''.join(ol)
  155. if outstr.startswith(' + '):
  156. outstr = outstr[3:]
  157. elif outstr.startswith(' '):
  158. outstr = outstr[1:]
  159. return outstr
  160. def _pretty(self, printer):
  161. e = self
  162. class Fake:
  163. baseline = 0
  164. def render(self, *args, **kwargs):
  165. ar = e.args # just to shorten things
  166. mpp = printer
  167. if len(ar) == 0:
  168. return str(0)
  169. bar = "\N{CIRCLED TIMES}" if printer._use_unicode else "|"
  170. ol = [] # output list, to be concatenated to a string
  171. for i, v in enumerate(ar):
  172. # if the coef of the dyadic is 1, we skip the 1
  173. if ar[i][0] == 1:
  174. ol.extend([" + ",
  175. mpp.doprint(ar[i][1]),
  176. bar,
  177. mpp.doprint(ar[i][2])])
  178. # if the coef of the dyadic is -1, we skip the 1
  179. elif ar[i][0] == -1:
  180. ol.extend([" - ",
  181. mpp.doprint(ar[i][1]),
  182. bar,
  183. mpp.doprint(ar[i][2])])
  184. # If the coefficient of the dyadic is not 1 or -1,
  185. # we might wrap it in parentheses, for readability.
  186. elif ar[i][0] != 0:
  187. if isinstance(ar[i][0], Add):
  188. arg_str = mpp._print(
  189. ar[i][0]).parens()[0]
  190. else:
  191. arg_str = mpp.doprint(ar[i][0])
  192. if arg_str.startswith("-"):
  193. arg_str = arg_str[1:]
  194. str_start = " - "
  195. else:
  196. str_start = " + "
  197. ol.extend([str_start, arg_str, " ",
  198. mpp.doprint(ar[i][1]),
  199. bar,
  200. mpp.doprint(ar[i][2])])
  201. outstr = "".join(ol)
  202. if outstr.startswith(" + "):
  203. outstr = outstr[3:]
  204. elif outstr.startswith(" "):
  205. outstr = outstr[1:]
  206. return outstr
  207. return Fake()
  208. def __rand__(self, other):
  209. """The inner product operator for a Vector or Dyadic, and a Dyadic
  210. This is for: Vector dot Dyadic
  211. Parameters
  212. ==========
  213. other : Vector
  214. The vector we are dotting with
  215. Examples
  216. ========
  217. >>> from sympy.physics.vector import ReferenceFrame, dot, outer
  218. >>> N = ReferenceFrame('N')
  219. >>> d = outer(N.x, N.x)
  220. >>> dot(N.x, d)
  221. N.x
  222. """
  223. from sympy.physics.vector.vector import Vector, _check_vector
  224. other = _check_vector(other)
  225. ol = Vector(0)
  226. for i, v in enumerate(self.args):
  227. ol += v[0] * v[2] * (v[1] & other)
  228. return ol
  229. def __rsub__(self, other):
  230. return (-1 * self) + other
  231. def __rxor__(self, other):
  232. """For a cross product in the form: Vector x Dyadic
  233. Parameters
  234. ==========
  235. other : Vector
  236. The Vector that we are crossing this Dyadic with
  237. Examples
  238. ========
  239. >>> from sympy.physics.vector import ReferenceFrame, outer, cross
  240. >>> N = ReferenceFrame('N')
  241. >>> d = outer(N.x, N.x)
  242. >>> cross(N.y, d)
  243. - (N.z|N.x)
  244. """
  245. from sympy.physics.vector.vector import _check_vector
  246. other = _check_vector(other)
  247. ol = Dyadic(0)
  248. for i, v in enumerate(self.args):
  249. ol += v[0] * ((other ^ v[1]) | v[2])
  250. return ol
  251. def _sympystr(self, printer):
  252. """Printing method. """
  253. ar = self.args # just to shorten things
  254. if len(ar) == 0:
  255. return printer._print(0)
  256. ol = [] # output list, to be concatenated to a string
  257. for i, v in enumerate(ar):
  258. # if the coef of the dyadic is 1, we skip the 1
  259. if ar[i][0] == 1:
  260. ol.append(' + (' + printer._print(ar[i][1]) + '|' + printer._print(ar[i][2]) + ')')
  261. # if the coef of the dyadic is -1, we skip the 1
  262. elif ar[i][0] == -1:
  263. ol.append(' - (' + printer._print(ar[i][1]) + '|' + printer._print(ar[i][2]) + ')')
  264. # If the coefficient of the dyadic is not 1 or -1,
  265. # we might wrap it in parentheses, for readability.
  266. elif ar[i][0] != 0:
  267. arg_str = printer._print(ar[i][0])
  268. if isinstance(ar[i][0], Add):
  269. arg_str = "(%s)" % arg_str
  270. if arg_str[0] == '-':
  271. arg_str = arg_str[1:]
  272. str_start = ' - '
  273. else:
  274. str_start = ' + '
  275. ol.append(str_start + arg_str + '*(' + printer._print(ar[i][1]) +
  276. '|' + printer._print(ar[i][2]) + ')')
  277. outstr = ''.join(ol)
  278. if outstr.startswith(' + '):
  279. outstr = outstr[3:]
  280. elif outstr.startswith(' '):
  281. outstr = outstr[1:]
  282. return outstr
  283. def __sub__(self, other):
  284. """The subtraction operator. """
  285. return self.__add__(other * -1)
  286. def __xor__(self, other):
  287. """For a cross product in the form: Dyadic x Vector.
  288. Parameters
  289. ==========
  290. other : Vector
  291. The Vector that we are crossing this Dyadic with
  292. Examples
  293. ========
  294. >>> from sympy.physics.vector import ReferenceFrame, outer, cross
  295. >>> N = ReferenceFrame('N')
  296. >>> d = outer(N.x, N.x)
  297. >>> cross(d, N.y)
  298. (N.x|N.z)
  299. """
  300. from sympy.physics.vector.vector import _check_vector
  301. other = _check_vector(other)
  302. ol = Dyadic(0)
  303. for i, v in enumerate(self.args):
  304. ol += v[0] * (v[1] | (v[2] ^ other))
  305. return ol
  306. __radd__ = __add__
  307. __rmul__ = __mul__
  308. def express(self, frame1, frame2=None):
  309. """Expresses this Dyadic in alternate frame(s)
  310. The first frame is the list side expression, the second frame is the
  311. right side; if Dyadic is in form A.x|B.y, you can express it in two
  312. different frames. If no second frame is given, the Dyadic is
  313. expressed in only one frame.
  314. Calls the global express function
  315. Parameters
  316. ==========
  317. frame1 : ReferenceFrame
  318. The frame to express the left side of the Dyadic in
  319. frame2 : ReferenceFrame
  320. If provided, the frame to express the right side of the Dyadic in
  321. Examples
  322. ========
  323. >>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
  324. >>> from sympy.physics.vector import init_vprinting
  325. >>> init_vprinting(pretty_print=False)
  326. >>> N = ReferenceFrame('N')
  327. >>> q = dynamicsymbols('q')
  328. >>> B = N.orientnew('B', 'Axis', [q, N.z])
  329. >>> d = outer(N.x, N.x)
  330. >>> d.express(B, N)
  331. cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x)
  332. """
  333. from sympy.physics.vector.functions import express
  334. return express(self, frame1, frame2)
  335. def to_matrix(self, reference_frame, second_reference_frame=None):
  336. """Returns the matrix form of the dyadic with respect to one or two
  337. reference frames.
  338. Parameters
  339. ----------
  340. reference_frame : ReferenceFrame
  341. The reference frame that the rows and columns of the matrix
  342. correspond to. If a second reference frame is provided, this
  343. only corresponds to the rows of the matrix.
  344. second_reference_frame : ReferenceFrame, optional, default=None
  345. The reference frame that the columns of the matrix correspond
  346. to.
  347. Returns
  348. -------
  349. matrix : ImmutableMatrix, shape(3,3)
  350. The matrix that gives the 2D tensor form.
  351. Examples
  352. ========
  353. >>> from sympy import symbols
  354. >>> from sympy.physics.vector import ReferenceFrame, Vector
  355. >>> Vector.simp = True
  356. >>> from sympy.physics.mechanics import inertia
  357. >>> Ixx, Iyy, Izz, Ixy, Iyz, Ixz = symbols('Ixx, Iyy, Izz, Ixy, Iyz, Ixz')
  358. >>> N = ReferenceFrame('N')
  359. >>> inertia_dyadic = inertia(N, Ixx, Iyy, Izz, Ixy, Iyz, Ixz)
  360. >>> inertia_dyadic.to_matrix(N)
  361. Matrix([
  362. [Ixx, Ixy, Ixz],
  363. [Ixy, Iyy, Iyz],
  364. [Ixz, Iyz, Izz]])
  365. >>> beta = symbols('beta')
  366. >>> A = N.orientnew('A', 'Axis', (beta, N.x))
  367. >>> inertia_dyadic.to_matrix(A)
  368. Matrix([
  369. [ Ixx, Ixy*cos(beta) + Ixz*sin(beta), -Ixy*sin(beta) + Ixz*cos(beta)],
  370. [ Ixy*cos(beta) + Ixz*sin(beta), Iyy*cos(2*beta)/2 + Iyy/2 + Iyz*sin(2*beta) - Izz*cos(2*beta)/2 + Izz/2, -Iyy*sin(2*beta)/2 + Iyz*cos(2*beta) + Izz*sin(2*beta)/2],
  371. [-Ixy*sin(beta) + Ixz*cos(beta), -Iyy*sin(2*beta)/2 + Iyz*cos(2*beta) + Izz*sin(2*beta)/2, -Iyy*cos(2*beta)/2 + Iyy/2 - Iyz*sin(2*beta) + Izz*cos(2*beta)/2 + Izz/2]])
  372. """
  373. if second_reference_frame is None:
  374. second_reference_frame = reference_frame
  375. return Matrix([i.dot(self).dot(j) for i in reference_frame for j in
  376. second_reference_frame]).reshape(3, 3)
  377. def doit(self, **hints):
  378. """Calls .doit() on each term in the Dyadic"""
  379. return sum([Dyadic([(v[0].doit(**hints), v[1], v[2])])
  380. for v in self.args], Dyadic(0))
  381. def dt(self, frame):
  382. """Take the time derivative of this Dyadic in a frame.
  383. This function calls the global time_derivative method
  384. Parameters
  385. ==========
  386. frame : ReferenceFrame
  387. The frame to take the time derivative in
  388. Examples
  389. ========
  390. >>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
  391. >>> from sympy.physics.vector import init_vprinting
  392. >>> init_vprinting(pretty_print=False)
  393. >>> N = ReferenceFrame('N')
  394. >>> q = dynamicsymbols('q')
  395. >>> B = N.orientnew('B', 'Axis', [q, N.z])
  396. >>> d = outer(N.x, N.x)
  397. >>> d.dt(B)
  398. - q'*(N.y|N.x) - q'*(N.x|N.y)
  399. """
  400. from sympy.physics.vector.functions import time_derivative
  401. return time_derivative(self, frame)
  402. def simplify(self):
  403. """Returns a simplified Dyadic."""
  404. out = Dyadic(0)
  405. for v in self.args:
  406. out += Dyadic([(v[0].simplify(), v[1], v[2])])
  407. return out
  408. def subs(self, *args, **kwargs):
  409. """Substitution on the Dyadic.
  410. Examples
  411. ========
  412. >>> from sympy.physics.vector import ReferenceFrame
  413. >>> from sympy import Symbol
  414. >>> N = ReferenceFrame('N')
  415. >>> s = Symbol('s')
  416. >>> a = s*(N.x|N.x)
  417. >>> a.subs({s: 2})
  418. 2*(N.x|N.x)
  419. """
  420. return sum([Dyadic([(v[0].subs(*args, **kwargs), v[1], v[2])])
  421. for v in self.args], Dyadic(0))
  422. def applyfunc(self, f):
  423. """Apply a function to each component of a Dyadic."""
  424. if not callable(f):
  425. raise TypeError("`f` must be callable.")
  426. out = Dyadic(0)
  427. for a, b, c in self.args:
  428. out += f(a) * (b|c)
  429. return out
  430. dot = __and__
  431. cross = __xor__
  432. def _eval_evalf(self, prec):
  433. if not self.args:
  434. return self
  435. new_args = []
  436. dps = prec_to_dps(prec)
  437. for inlist in self.args:
  438. new_inlist = list(inlist)
  439. new_inlist[0] = inlist[0].evalf(n=dps)
  440. new_args.append(tuple(new_inlist))
  441. return Dyadic(new_args)
  442. def xreplace(self, rule):
  443. """
  444. Replace occurrences of objects within the measure numbers of the Dyadic.
  445. Parameters
  446. ==========
  447. rule : dict-like
  448. Expresses a replacement rule.
  449. Returns
  450. =======
  451. Dyadic
  452. Result of the replacement.
  453. Examples
  454. ========
  455. >>> from sympy import symbols, pi
  456. >>> from sympy.physics.vector import ReferenceFrame, outer
  457. >>> N = ReferenceFrame('N')
  458. >>> D = outer(N.x, N.x)
  459. >>> x, y, z = symbols('x y z')
  460. >>> ((1 + x*y) * D).xreplace({x: pi})
  461. (pi*y + 1)*(N.x|N.x)
  462. >>> ((1 + x*y) * D).xreplace({x: pi, y: 2})
  463. (1 + 2*pi)*(N.x|N.x)
  464. Replacements occur only if an entire node in the expression tree is
  465. matched:
  466. >>> ((x*y + z) * D).xreplace({x*y: pi})
  467. (z + pi)*(N.x|N.x)
  468. >>> ((x*y*z) * D).xreplace({x*y: pi})
  469. x*y*z*(N.x|N.x)
  470. """
  471. new_args = []
  472. for inlist in self.args:
  473. new_inlist = list(inlist)
  474. new_inlist[0] = new_inlist[0].xreplace(rule)
  475. new_args.append(tuple(new_inlist))
  476. return Dyadic(new_args)
  477. def _check_dyadic(other):
  478. if not isinstance(other, Dyadic):
  479. raise TypeError('A Dyadic must be supplied')
  480. return other