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.

673 lines
20 KiB

6 months ago
  1. from sympy.core import S, sympify, diff
  2. from sympy.core.function import Function, ArgumentIndexError
  3. from sympy.core.logic import fuzzy_not
  4. from sympy.core.relational import Eq, Ne
  5. from sympy.functions.elementary.complexes import im, sign
  6. from sympy.functions.elementary.piecewise import Piecewise
  7. from sympy.polys.polyerrors import PolynomialError
  8. from sympy.utilities.misc import filldedent
  9. ###############################################################################
  10. ################################ DELTA FUNCTION ###############################
  11. ###############################################################################
  12. class DiracDelta(Function):
  13. r"""
  14. The DiracDelta function and its derivatives.
  15. Explanation
  16. ===========
  17. DiracDelta is not an ordinary function. It can be rigorously defined either
  18. as a distribution or as a measure.
  19. DiracDelta only makes sense in definite integrals, and in particular,
  20. integrals of the form ``Integral(f(x)*DiracDelta(x - x0), (x, a, b))``,
  21. where it equals ``f(x0)`` if ``a <= x0 <= b`` and ``0`` otherwise. Formally,
  22. DiracDelta acts in some ways like a function that is ``0`` everywhere except
  23. at ``0``, but in many ways it also does not. It can often be useful to treat
  24. DiracDelta in formal ways, building up and manipulating expressions with
  25. delta functions (which may eventually be integrated), but care must be taken
  26. to not treat it as a real function. SymPy's ``oo`` is similar. It only
  27. truly makes sense formally in certain contexts (such as integration limits),
  28. but SymPy allows its use everywhere, and it tries to be consistent with
  29. operations on it (like ``1/oo``), but it is easy to get into trouble and get
  30. wrong results if ``oo`` is treated too much like a number. Similarly, if
  31. DiracDelta is treated too much like a function, it is easy to get wrong or
  32. nonsensical results.
  33. DiracDelta function has the following properties:
  34. 1) $\frac{d}{d x} \theta(x) = \delta(x)$
  35. 2) $\int_{-\infty}^\infty \delta(x - a)f(x)\, dx = f(a)$ and $\int_{a-
  36. \epsilon}^{a+\epsilon} \delta(x - a)f(x)\, dx = f(a)$
  37. 3) $\delta(x) = 0$ for all $x \neq 0$
  38. 4) $\delta(g(x)) = \sum_i \frac{\delta(x - x_i)}{\|g'(x_i)\|}$ where $x_i$
  39. are the roots of $g$
  40. 5) $\delta(-x) = \delta(x)$
  41. Derivatives of ``k``-th order of DiracDelta have the following properties:
  42. 6) $\delta(x, k) = 0$ for all $x \neq 0$
  43. 7) $\delta(-x, k) = -\delta(x, k)$ for odd $k$
  44. 8) $\delta(-x, k) = \delta(x, k)$ for even $k$
  45. Examples
  46. ========
  47. >>> from sympy import DiracDelta, diff, pi
  48. >>> from sympy.abc import x, y
  49. >>> DiracDelta(x)
  50. DiracDelta(x)
  51. >>> DiracDelta(1)
  52. 0
  53. >>> DiracDelta(-1)
  54. 0
  55. >>> DiracDelta(pi)
  56. 0
  57. >>> DiracDelta(x - 4).subs(x, 4)
  58. DiracDelta(0)
  59. >>> diff(DiracDelta(x))
  60. DiracDelta(x, 1)
  61. >>> diff(DiracDelta(x - 1),x,2)
  62. DiracDelta(x - 1, 2)
  63. >>> diff(DiracDelta(x**2 - 1),x,2)
  64. 2*(2*x**2*DiracDelta(x**2 - 1, 2) + DiracDelta(x**2 - 1, 1))
  65. >>> DiracDelta(3*x).is_simple(x)
  66. True
  67. >>> DiracDelta(x**2).is_simple(x)
  68. False
  69. >>> DiracDelta((x**2 - 1)*y).expand(diracdelta=True, wrt=x)
  70. DiracDelta(x - 1)/(2*Abs(y)) + DiracDelta(x + 1)/(2*Abs(y))
  71. See Also
  72. ========
  73. Heaviside
  74. sympy.simplify.simplify.simplify, is_simple
  75. sympy.functions.special.tensor_functions.KroneckerDelta
  76. References
  77. ==========
  78. .. [1] http://mathworld.wolfram.com/DeltaFunction.html
  79. """
  80. is_real = True
  81. def fdiff(self, argindex=1):
  82. """
  83. Returns the first derivative of a DiracDelta Function.
  84. Explanation
  85. ===========
  86. The difference between ``diff()`` and ``fdiff()`` is: ``diff()`` is the
  87. user-level function and ``fdiff()`` is an object method. ``fdiff()`` is
  88. a convenience method available in the ``Function`` class. It returns
  89. the derivative of the function without considering the chain rule.
  90. ``diff(function, x)`` calls ``Function._eval_derivative`` which in turn
  91. calls ``fdiff()`` internally to compute the derivative of the function.
  92. Examples
  93. ========
  94. >>> from sympy import DiracDelta, diff
  95. >>> from sympy.abc import x
  96. >>> DiracDelta(x).fdiff()
  97. DiracDelta(x, 1)
  98. >>> DiracDelta(x, 1).fdiff()
  99. DiracDelta(x, 2)
  100. >>> DiracDelta(x**2 - 1).fdiff()
  101. DiracDelta(x**2 - 1, 1)
  102. >>> diff(DiracDelta(x, 1)).fdiff()
  103. DiracDelta(x, 3)
  104. Parameters
  105. ==========
  106. argindex : integer
  107. degree of derivative
  108. """
  109. if argindex == 1:
  110. #I didn't know if there is a better way to handle default arguments
  111. k = 0
  112. if len(self.args) > 1:
  113. k = self.args[1]
  114. return self.func(self.args[0], k + 1)
  115. else:
  116. raise ArgumentIndexError(self, argindex)
  117. @classmethod
  118. def eval(cls, arg, k=0):
  119. """
  120. Returns a simplified form or a value of DiracDelta depending on the
  121. argument passed by the DiracDelta object.
  122. Explanation
  123. ===========
  124. The ``eval()`` method is automatically called when the ``DiracDelta``
  125. class is about to be instantiated and it returns either some simplified
  126. instance or the unevaluated instance depending on the argument passed.
  127. In other words, ``eval()`` method is not needed to be called explicitly,
  128. it is being called and evaluated once the object is called.
  129. Examples
  130. ========
  131. >>> from sympy import DiracDelta, S
  132. >>> from sympy.abc import x
  133. >>> DiracDelta(x)
  134. DiracDelta(x)
  135. >>> DiracDelta(-x, 1)
  136. -DiracDelta(x, 1)
  137. >>> DiracDelta(1)
  138. 0
  139. >>> DiracDelta(5, 1)
  140. 0
  141. >>> DiracDelta(0)
  142. DiracDelta(0)
  143. >>> DiracDelta(-1)
  144. 0
  145. >>> DiracDelta(S.NaN)
  146. nan
  147. >>> DiracDelta(x).eval(1)
  148. 0
  149. >>> DiracDelta(x - 100).subs(x, 5)
  150. 0
  151. >>> DiracDelta(x - 100).subs(x, 100)
  152. DiracDelta(0)
  153. Parameters
  154. ==========
  155. k : integer
  156. order of derivative
  157. arg : argument passed to DiracDelta
  158. """
  159. k = sympify(k)
  160. if not k.is_Integer or k.is_negative:
  161. raise ValueError("Error: the second argument of DiracDelta must be \
  162. a non-negative integer, %s given instead." % (k,))
  163. arg = sympify(arg)
  164. if arg is S.NaN:
  165. return S.NaN
  166. if arg.is_nonzero:
  167. return S.Zero
  168. if fuzzy_not(im(arg).is_zero):
  169. raise ValueError(filldedent('''
  170. Function defined only for Real Values.
  171. Complex part: %s found in %s .''' % (
  172. repr(im(arg)), repr(arg))))
  173. c, nc = arg.args_cnc()
  174. if c and c[0] is S.NegativeOne:
  175. # keep this fast and simple instead of using
  176. # could_extract_minus_sign
  177. if k.is_odd:
  178. return -cls(-arg, k)
  179. elif k.is_even:
  180. return cls(-arg, k) if k else cls(-arg)
  181. def _eval_expand_diracdelta(self, **hints):
  182. """
  183. Compute a simplified representation of the function using
  184. property number 4. Pass ``wrt`` as a hint to expand the expression
  185. with respect to a particular variable.
  186. Explanation
  187. ===========
  188. ``wrt`` is:
  189. - a variable with respect to which a DiracDelta expression will
  190. get expanded.
  191. Examples
  192. ========
  193. >>> from sympy import DiracDelta
  194. >>> from sympy.abc import x, y
  195. >>> DiracDelta(x*y).expand(diracdelta=True, wrt=x)
  196. DiracDelta(x)/Abs(y)
  197. >>> DiracDelta(x*y).expand(diracdelta=True, wrt=y)
  198. DiracDelta(y)/Abs(x)
  199. >>> DiracDelta(x**2 + x - 2).expand(diracdelta=True, wrt=x)
  200. DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3
  201. See Also
  202. ========
  203. is_simple, Diracdelta
  204. """
  205. from sympy.polys.polyroots import roots
  206. wrt = hints.get('wrt', None)
  207. if wrt is None:
  208. free = self.free_symbols
  209. if len(free) == 1:
  210. wrt = free.pop()
  211. else:
  212. raise TypeError(filldedent('''
  213. When there is more than 1 free symbol or variable in the expression,
  214. the 'wrt' keyword is required as a hint to expand when using the
  215. DiracDelta hint.'''))
  216. if not self.args[0].has(wrt) or (len(self.args) > 1 and self.args[1] != 0 ):
  217. return self
  218. try:
  219. argroots = roots(self.args[0], wrt)
  220. result = 0
  221. valid = True
  222. darg = abs(diff(self.args[0], wrt))
  223. for r, m in argroots.items():
  224. if r.is_real is not False and m == 1:
  225. result += self.func(wrt - r)/darg.subs(wrt, r)
  226. else:
  227. # don't handle non-real and if m != 1 then
  228. # a polynomial will have a zero in the derivative (darg)
  229. # at r
  230. valid = False
  231. break
  232. if valid:
  233. return result
  234. except PolynomialError:
  235. pass
  236. return self
  237. def is_simple(self, x):
  238. """
  239. Tells whether the argument(args[0]) of DiracDelta is a linear
  240. expression in *x*.
  241. Examples
  242. ========
  243. >>> from sympy import DiracDelta, cos
  244. >>> from sympy.abc import x, y
  245. >>> DiracDelta(x*y).is_simple(x)
  246. True
  247. >>> DiracDelta(x*y).is_simple(y)
  248. True
  249. >>> DiracDelta(x**2 + x - 2).is_simple(x)
  250. False
  251. >>> DiracDelta(cos(x)).is_simple(x)
  252. False
  253. Parameters
  254. ==========
  255. x : can be a symbol
  256. See Also
  257. ========
  258. sympy.simplify.simplify.simplify, DiracDelta
  259. """
  260. p = self.args[0].as_poly(x)
  261. if p:
  262. return p.degree() == 1
  263. return False
  264. def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
  265. """
  266. Represents DiracDelta in a piecewise form.
  267. Examples
  268. ========
  269. >>> from sympy import DiracDelta, Piecewise, Symbol
  270. >>> x = Symbol('x')
  271. >>> DiracDelta(x).rewrite(Piecewise)
  272. Piecewise((DiracDelta(0), Eq(x, 0)), (0, True))
  273. >>> DiracDelta(x - 5).rewrite(Piecewise)
  274. Piecewise((DiracDelta(0), Eq(x, 5)), (0, True))
  275. >>> DiracDelta(x**2 - 5).rewrite(Piecewise)
  276. Piecewise((DiracDelta(0), Eq(x**2, 5)), (0, True))
  277. >>> DiracDelta(x - 5, 4).rewrite(Piecewise)
  278. DiracDelta(x - 5, 4)
  279. """
  280. if len(args) == 1:
  281. return Piecewise((DiracDelta(0), Eq(args[0], 0)), (0, True))
  282. def _eval_rewrite_as_SingularityFunction(self, *args, **kwargs):
  283. """
  284. Returns the DiracDelta expression written in the form of Singularity
  285. Functions.
  286. """
  287. from sympy.solvers import solve
  288. from sympy.functions import SingularityFunction
  289. if self == DiracDelta(0):
  290. return SingularityFunction(0, 0, -1)
  291. if self == DiracDelta(0, 1):
  292. return SingularityFunction(0, 0, -2)
  293. free = self.free_symbols
  294. if len(free) == 1:
  295. x = (free.pop())
  296. if len(args) == 1:
  297. return SingularityFunction(x, solve(args[0], x)[0], -1)
  298. return SingularityFunction(x, solve(args[0], x)[0], -args[1] - 1)
  299. else:
  300. # I don't know how to handle the case for DiracDelta expressions
  301. # having arguments with more than one variable.
  302. raise TypeError(filldedent('''
  303. rewrite(SingularityFunction) doesn't support
  304. arguments with more that 1 variable.'''))
  305. ###############################################################################
  306. ############################## HEAVISIDE FUNCTION #############################
  307. ###############################################################################
  308. class Heaviside(Function):
  309. r"""
  310. Heaviside step function.
  311. Explanation
  312. ===========
  313. The Heaviside step function has the following properties:
  314. 1) $\frac{d}{d x} \theta(x) = \delta(x)$
  315. 2) $\theta(x) = \begin{cases} 0 & \text{for}\: x < 0 \\ \frac{1}{2} &
  316. \text{for}\: x = 0 \\1 & \text{for}\: x > 0 \end{cases}$
  317. 3) $\frac{d}{d x} \max(x, 0) = \theta(x)$
  318. Heaviside(x) is printed as $\theta(x)$ with the SymPy LaTeX printer.
  319. The value at 0 is set differently in different fields. SymPy uses 1/2,
  320. which is a convention from electronics and signal processing, and is
  321. consistent with solving improper integrals by Fourier transform and
  322. convolution.
  323. To specify a different value of Heaviside at ``x=0``, a second argument
  324. can be given. Using ``Heaviside(x, nan)`` gives an expression that will
  325. evaluate to nan for x=0.
  326. .. versionchanged:: 1.9 ``Heaviside(0)`` now returns 1/2 (before: undefined)
  327. Examples
  328. ========
  329. >>> from sympy import Heaviside, nan
  330. >>> from sympy.abc import x
  331. >>> Heaviside(9)
  332. 1
  333. >>> Heaviside(-9)
  334. 0
  335. >>> Heaviside(0)
  336. 1/2
  337. >>> Heaviside(0, nan)
  338. nan
  339. >>> (Heaviside(x) + 1).replace(Heaviside(x), Heaviside(x, 1))
  340. Heaviside(x, 1) + 1
  341. See Also
  342. ========
  343. DiracDelta
  344. References
  345. ==========
  346. .. [1] http://mathworld.wolfram.com/HeavisideStepFunction.html
  347. .. [2] http://dlmf.nist.gov/1.16#iv
  348. """
  349. is_real = True
  350. def fdiff(self, argindex=1):
  351. """
  352. Returns the first derivative of a Heaviside Function.
  353. Examples
  354. ========
  355. >>> from sympy import Heaviside, diff
  356. >>> from sympy.abc import x
  357. >>> Heaviside(x).fdiff()
  358. DiracDelta(x)
  359. >>> Heaviside(x**2 - 1).fdiff()
  360. DiracDelta(x**2 - 1)
  361. >>> diff(Heaviside(x)).fdiff()
  362. DiracDelta(x, 1)
  363. Parameters
  364. ==========
  365. argindex : integer
  366. order of derivative
  367. """
  368. if argindex == 1:
  369. return DiracDelta(self.args[0])
  370. else:
  371. raise ArgumentIndexError(self, argindex)
  372. def __new__(cls, arg, H0=S.Half, **options):
  373. if isinstance(H0, Heaviside) and len(H0.args) == 1:
  374. H0 = S.Half
  375. return super(cls, cls).__new__(cls, arg, H0, **options)
  376. @property
  377. def pargs(self):
  378. """Args without default S.Half"""
  379. args = self.args
  380. if args[1] is S.Half:
  381. args = args[:1]
  382. return args
  383. @classmethod
  384. def eval(cls, arg, H0=S.Half):
  385. """
  386. Returns a simplified form or a value of Heaviside depending on the
  387. argument passed by the Heaviside object.
  388. Explanation
  389. ===========
  390. The ``eval()`` method is automatically called when the ``Heaviside``
  391. class is about to be instantiated and it returns either some simplified
  392. instance or the unevaluated instance depending on the argument passed.
  393. In other words, ``eval()`` method is not needed to be called explicitly,
  394. it is being called and evaluated once the object is called.
  395. Examples
  396. ========
  397. >>> from sympy import Heaviside, S
  398. >>> from sympy.abc import x
  399. >>> Heaviside(x)
  400. Heaviside(x)
  401. >>> Heaviside(19)
  402. 1
  403. >>> Heaviside(0)
  404. 1/2
  405. >>> Heaviside(0, 1)
  406. 1
  407. >>> Heaviside(-5)
  408. 0
  409. >>> Heaviside(S.NaN)
  410. nan
  411. >>> Heaviside(x).eval(42)
  412. 1
  413. >>> Heaviside(x - 100).subs(x, 5)
  414. 0
  415. >>> Heaviside(x - 100).subs(x, 105)
  416. 1
  417. Parameters
  418. ==========
  419. arg : argument passed by Heaviside object
  420. H0 : value of Heaviside(0)
  421. """
  422. H0 = sympify(H0)
  423. arg = sympify(arg)
  424. if arg.is_extended_negative:
  425. return S.Zero
  426. elif arg.is_extended_positive:
  427. return S.One
  428. elif arg.is_zero:
  429. return H0
  430. elif arg is S.NaN:
  431. return S.NaN
  432. elif fuzzy_not(im(arg).is_zero):
  433. raise ValueError("Function defined only for Real Values. Complex part: %s found in %s ." % (repr(im(arg)), repr(arg)) )
  434. def _eval_rewrite_as_Piecewise(self, arg, H0=None, **kwargs):
  435. """
  436. Represents Heaviside in a Piecewise form.
  437. Examples
  438. ========
  439. >>> from sympy import Heaviside, Piecewise, Symbol, nan
  440. >>> x = Symbol('x')
  441. >>> Heaviside(x).rewrite(Piecewise)
  442. Piecewise((0, x < 0), (1/2, Eq(x, 0)), (1, x > 0))
  443. >>> Heaviside(x,nan).rewrite(Piecewise)
  444. Piecewise((0, x < 0), (nan, Eq(x, 0)), (1, x > 0))
  445. >>> Heaviside(x - 5).rewrite(Piecewise)
  446. Piecewise((0, x < 5), (1/2, Eq(x, 5)), (1, x > 5))
  447. >>> Heaviside(x**2 - 1).rewrite(Piecewise)
  448. Piecewise((0, x**2 < 1), (1/2, Eq(x**2, 1)), (1, x**2 > 1))
  449. """
  450. if H0 == 0:
  451. return Piecewise((0, arg <= 0), (1, arg > 0))
  452. if H0 == 1:
  453. return Piecewise((0, arg < 0), (1, arg >= 0))
  454. return Piecewise((0, arg < 0), (H0, Eq(arg, 0)), (1, arg > 0))
  455. def _eval_rewrite_as_sign(self, arg, H0=S.Half, **kwargs):
  456. """
  457. Represents the Heaviside function in the form of sign function.
  458. Explanation
  459. ===========
  460. The value of Heaviside(0) must be 1/2 for rewritting as sign to be
  461. strictly equivalent. For easier usage, we also allow this rewriting
  462. when Heaviside(0) is undefined.
  463. Examples
  464. ========
  465. >>> from sympy import Heaviside, Symbol, sign, nan
  466. >>> x = Symbol('x', real=True)
  467. >>> y = Symbol('y')
  468. >>> Heaviside(x).rewrite(sign)
  469. sign(x)/2 + 1/2
  470. >>> Heaviside(x, 0).rewrite(sign)
  471. Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (0, True))
  472. >>> Heaviside(x, nan).rewrite(sign)
  473. Piecewise((sign(x)/2 + 1/2, Ne(x, 0)), (nan, True))
  474. >>> Heaviside(x - 2).rewrite(sign)
  475. sign(x - 2)/2 + 1/2
  476. >>> Heaviside(x**2 - 2*x + 1).rewrite(sign)
  477. sign(x**2 - 2*x + 1)/2 + 1/2
  478. >>> Heaviside(y).rewrite(sign)
  479. Heaviside(y)
  480. >>> Heaviside(y**2 - 2*y + 1).rewrite(sign)
  481. Heaviside(y**2 - 2*y + 1)
  482. See Also
  483. ========
  484. sign
  485. """
  486. if arg.is_extended_real:
  487. pw1 = Piecewise(
  488. ((sign(arg) + 1)/2, Ne(arg, 0)),
  489. (Heaviside(0, H0=H0), True))
  490. pw2 = Piecewise(
  491. ((sign(arg) + 1)/2, Eq(Heaviside(0, H0=H0), S.Half)),
  492. (pw1, True))
  493. return pw2
  494. def _eval_rewrite_as_SingularityFunction(self, args, H0=S.Half, **kwargs):
  495. """
  496. Returns the Heaviside expression written in the form of Singularity
  497. Functions.
  498. """
  499. from sympy.solvers import solve
  500. from sympy.functions import SingularityFunction
  501. if self == Heaviside(0):
  502. return SingularityFunction(0, 0, 0)
  503. free = self.free_symbols
  504. if len(free) == 1:
  505. x = (free.pop())
  506. return SingularityFunction(x, solve(args, x)[0], 0)
  507. # TODO
  508. # ((x - 5)**3*Heaviside(x - 5)).rewrite(SingularityFunction) should output
  509. # SingularityFunction(x, 5, 0) instead of (x - 5)**3*SingularityFunction(x, 5, 0)
  510. else:
  511. # I don't know how to handle the case for Heaviside expressions
  512. # having arguments with more than one variable.
  513. raise TypeError(filldedent('''
  514. rewrite(SingularityFunction) doesn't
  515. support arguments with more that 1 variable.'''))