tensor_functions.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. from math import prod
  2. from sympy.core import S, Integer
  3. from sympy.core.function import Function
  4. from sympy.core.logic import fuzzy_not
  5. from sympy.core.relational import Ne
  6. from sympy.core.sorting import default_sort_key
  7. from sympy.external.gmpy import SYMPY_INTS
  8. from sympy.functions.combinatorial.factorials import factorial
  9. from sympy.functions.elementary.piecewise import Piecewise
  10. from sympy.utilities.iterables import has_dups
  11. ###############################################################################
  12. ###################### Kronecker Delta, Levi-Civita etc. ######################
  13. ###############################################################################
  14. def Eijk(*args, **kwargs):
  15. """
  16. Represent the Levi-Civita symbol.
  17. This is a compatibility wrapper to ``LeviCivita()``.
  18. See Also
  19. ========
  20. LeviCivita
  21. """
  22. return LeviCivita(*args, **kwargs)
  23. def eval_levicivita(*args):
  24. """Evaluate Levi-Civita symbol."""
  25. n = len(args)
  26. return prod(
  27. prod(args[j] - args[i] for j in range(i + 1, n))
  28. / factorial(i) for i in range(n))
  29. # converting factorial(i) to int is slightly faster
  30. class LeviCivita(Function):
  31. """
  32. Represent the Levi-Civita symbol.
  33. Explanation
  34. ===========
  35. For even permutations of indices it returns 1, for odd permutations -1, and
  36. for everything else (a repeated index) it returns 0.
  37. Thus it represents an alternating pseudotensor.
  38. Examples
  39. ========
  40. >>> from sympy import LeviCivita
  41. >>> from sympy.abc import i, j, k
  42. >>> LeviCivita(1, 2, 3)
  43. 1
  44. >>> LeviCivita(1, 3, 2)
  45. -1
  46. >>> LeviCivita(1, 2, 2)
  47. 0
  48. >>> LeviCivita(i, j, k)
  49. LeviCivita(i, j, k)
  50. >>> LeviCivita(i, j, i)
  51. 0
  52. See Also
  53. ========
  54. Eijk
  55. """
  56. is_integer = True
  57. @classmethod
  58. def eval(cls, *args):
  59. if all(isinstance(a, (SYMPY_INTS, Integer)) for a in args):
  60. return eval_levicivita(*args)
  61. if has_dups(args):
  62. return S.Zero
  63. def doit(self, **hints):
  64. return eval_levicivita(*self.args)
  65. class KroneckerDelta(Function):
  66. """
  67. The discrete, or Kronecker, delta function.
  68. Explanation
  69. ===========
  70. A function that takes in two integers $i$ and $j$. It returns $0$ if $i$
  71. and $j$ are not equal, or it returns $1$ if $i$ and $j$ are equal.
  72. Examples
  73. ========
  74. An example with integer indices:
  75. >>> from sympy import KroneckerDelta
  76. >>> KroneckerDelta(1, 2)
  77. 0
  78. >>> KroneckerDelta(3, 3)
  79. 1
  80. Symbolic indices:
  81. >>> from sympy.abc import i, j, k
  82. >>> KroneckerDelta(i, j)
  83. KroneckerDelta(i, j)
  84. >>> KroneckerDelta(i, i)
  85. 1
  86. >>> KroneckerDelta(i, i + 1)
  87. 0
  88. >>> KroneckerDelta(i, i + 1 + k)
  89. KroneckerDelta(i, i + k + 1)
  90. Parameters
  91. ==========
  92. i : Number, Symbol
  93. The first index of the delta function.
  94. j : Number, Symbol
  95. The second index of the delta function.
  96. See Also
  97. ========
  98. eval
  99. DiracDelta
  100. References
  101. ==========
  102. .. [1] https://en.wikipedia.org/wiki/Kronecker_delta
  103. """
  104. is_integer = True
  105. @classmethod
  106. def eval(cls, i, j, delta_range=None):
  107. """
  108. Evaluates the discrete delta function.
  109. Examples
  110. ========
  111. >>> from sympy import KroneckerDelta
  112. >>> from sympy.abc import i, j, k
  113. >>> KroneckerDelta(i, j)
  114. KroneckerDelta(i, j)
  115. >>> KroneckerDelta(i, i)
  116. 1
  117. >>> KroneckerDelta(i, i + 1)
  118. 0
  119. >>> KroneckerDelta(i, i + 1 + k)
  120. KroneckerDelta(i, i + k + 1)
  121. # indirect doctest
  122. """
  123. if delta_range is not None:
  124. dinf, dsup = delta_range
  125. if (dinf - i > 0) == True:
  126. return S.Zero
  127. if (dinf - j > 0) == True:
  128. return S.Zero
  129. if (dsup - i < 0) == True:
  130. return S.Zero
  131. if (dsup - j < 0) == True:
  132. return S.Zero
  133. diff = i - j
  134. if diff.is_zero:
  135. return S.One
  136. elif fuzzy_not(diff.is_zero):
  137. return S.Zero
  138. if i.assumptions0.get("below_fermi") and \
  139. j.assumptions0.get("above_fermi"):
  140. return S.Zero
  141. if j.assumptions0.get("below_fermi") and \
  142. i.assumptions0.get("above_fermi"):
  143. return S.Zero
  144. # to make KroneckerDelta canonical
  145. # following lines will check if inputs are in order
  146. # if not, will return KroneckerDelta with correct order
  147. if i != min(i, j, key=default_sort_key):
  148. if delta_range:
  149. return cls(j, i, delta_range)
  150. else:
  151. return cls(j, i)
  152. @property
  153. def delta_range(self):
  154. if len(self.args) > 2:
  155. return self.args[2]
  156. def _eval_power(self, expt):
  157. if expt.is_positive:
  158. return self
  159. if expt.is_negative and expt is not S.NegativeOne:
  160. return 1/self
  161. @property
  162. def is_above_fermi(self):
  163. """
  164. True if Delta can be non-zero above fermi.
  165. Examples
  166. ========
  167. >>> from sympy import KroneckerDelta, Symbol
  168. >>> a = Symbol('a', above_fermi=True)
  169. >>> i = Symbol('i', below_fermi=True)
  170. >>> p = Symbol('p')
  171. >>> q = Symbol('q')
  172. >>> KroneckerDelta(p, a).is_above_fermi
  173. True
  174. >>> KroneckerDelta(p, i).is_above_fermi
  175. False
  176. >>> KroneckerDelta(p, q).is_above_fermi
  177. True
  178. See Also
  179. ========
  180. is_below_fermi, is_only_below_fermi, is_only_above_fermi
  181. """
  182. if self.args[0].assumptions0.get("below_fermi"):
  183. return False
  184. if self.args[1].assumptions0.get("below_fermi"):
  185. return False
  186. return True
  187. @property
  188. def is_below_fermi(self):
  189. """
  190. True if Delta can be non-zero below fermi.
  191. Examples
  192. ========
  193. >>> from sympy import KroneckerDelta, Symbol
  194. >>> a = Symbol('a', above_fermi=True)
  195. >>> i = Symbol('i', below_fermi=True)
  196. >>> p = Symbol('p')
  197. >>> q = Symbol('q')
  198. >>> KroneckerDelta(p, a).is_below_fermi
  199. False
  200. >>> KroneckerDelta(p, i).is_below_fermi
  201. True
  202. >>> KroneckerDelta(p, q).is_below_fermi
  203. True
  204. See Also
  205. ========
  206. is_above_fermi, is_only_above_fermi, is_only_below_fermi
  207. """
  208. if self.args[0].assumptions0.get("above_fermi"):
  209. return False
  210. if self.args[1].assumptions0.get("above_fermi"):
  211. return False
  212. return True
  213. @property
  214. def is_only_above_fermi(self):
  215. """
  216. True if Delta is restricted to above fermi.
  217. Examples
  218. ========
  219. >>> from sympy import KroneckerDelta, Symbol
  220. >>> a = Symbol('a', above_fermi=True)
  221. >>> i = Symbol('i', below_fermi=True)
  222. >>> p = Symbol('p')
  223. >>> q = Symbol('q')
  224. >>> KroneckerDelta(p, a).is_only_above_fermi
  225. True
  226. >>> KroneckerDelta(p, q).is_only_above_fermi
  227. False
  228. >>> KroneckerDelta(p, i).is_only_above_fermi
  229. False
  230. See Also
  231. ========
  232. is_above_fermi, is_below_fermi, is_only_below_fermi
  233. """
  234. return ( self.args[0].assumptions0.get("above_fermi")
  235. or
  236. self.args[1].assumptions0.get("above_fermi")
  237. ) or False
  238. @property
  239. def is_only_below_fermi(self):
  240. """
  241. True if Delta is restricted to below fermi.
  242. Examples
  243. ========
  244. >>> from sympy import KroneckerDelta, Symbol
  245. >>> a = Symbol('a', above_fermi=True)
  246. >>> i = Symbol('i', below_fermi=True)
  247. >>> p = Symbol('p')
  248. >>> q = Symbol('q')
  249. >>> KroneckerDelta(p, i).is_only_below_fermi
  250. True
  251. >>> KroneckerDelta(p, q).is_only_below_fermi
  252. False
  253. >>> KroneckerDelta(p, a).is_only_below_fermi
  254. False
  255. See Also
  256. ========
  257. is_above_fermi, is_below_fermi, is_only_above_fermi
  258. """
  259. return ( self.args[0].assumptions0.get("below_fermi")
  260. or
  261. self.args[1].assumptions0.get("below_fermi")
  262. ) or False
  263. @property
  264. def indices_contain_equal_information(self):
  265. """
  266. Returns True if indices are either both above or below fermi.
  267. Examples
  268. ========
  269. >>> from sympy import KroneckerDelta, Symbol
  270. >>> a = Symbol('a', above_fermi=True)
  271. >>> i = Symbol('i', below_fermi=True)
  272. >>> p = Symbol('p')
  273. >>> q = Symbol('q')
  274. >>> KroneckerDelta(p, q).indices_contain_equal_information
  275. True
  276. >>> KroneckerDelta(p, q+1).indices_contain_equal_information
  277. True
  278. >>> KroneckerDelta(i, p).indices_contain_equal_information
  279. False
  280. """
  281. if (self.args[0].assumptions0.get("below_fermi") and
  282. self.args[1].assumptions0.get("below_fermi")):
  283. return True
  284. if (self.args[0].assumptions0.get("above_fermi")
  285. and self.args[1].assumptions0.get("above_fermi")):
  286. return True
  287. # if both indices are general we are True, else false
  288. return self.is_below_fermi and self.is_above_fermi
  289. @property
  290. def preferred_index(self):
  291. """
  292. Returns the index which is preferred to keep in the final expression.
  293. Explanation
  294. ===========
  295. The preferred index is the index with more information regarding fermi
  296. level. If indices contain the same information, 'a' is preferred before
  297. 'b'.
  298. Examples
  299. ========
  300. >>> from sympy import KroneckerDelta, Symbol
  301. >>> a = Symbol('a', above_fermi=True)
  302. >>> i = Symbol('i', below_fermi=True)
  303. >>> j = Symbol('j', below_fermi=True)
  304. >>> p = Symbol('p')
  305. >>> KroneckerDelta(p, i).preferred_index
  306. i
  307. >>> KroneckerDelta(p, a).preferred_index
  308. a
  309. >>> KroneckerDelta(i, j).preferred_index
  310. i
  311. See Also
  312. ========
  313. killable_index
  314. """
  315. if self._get_preferred_index():
  316. return self.args[1]
  317. else:
  318. return self.args[0]
  319. @property
  320. def killable_index(self):
  321. """
  322. Returns the index which is preferred to substitute in the final
  323. expression.
  324. Explanation
  325. ===========
  326. The index to substitute is the index with less information regarding
  327. fermi level. If indices contain the same information, 'a' is preferred
  328. before 'b'.
  329. Examples
  330. ========
  331. >>> from sympy import KroneckerDelta, Symbol
  332. >>> a = Symbol('a', above_fermi=True)
  333. >>> i = Symbol('i', below_fermi=True)
  334. >>> j = Symbol('j', below_fermi=True)
  335. >>> p = Symbol('p')
  336. >>> KroneckerDelta(p, i).killable_index
  337. p
  338. >>> KroneckerDelta(p, a).killable_index
  339. p
  340. >>> KroneckerDelta(i, j).killable_index
  341. j
  342. See Also
  343. ========
  344. preferred_index
  345. """
  346. if self._get_preferred_index():
  347. return self.args[0]
  348. else:
  349. return self.args[1]
  350. def _get_preferred_index(self):
  351. """
  352. Returns the index which is preferred to keep in the final expression.
  353. The preferred index is the index with more information regarding fermi
  354. level. If indices contain the same information, index 0 is returned.
  355. """
  356. if not self.is_above_fermi:
  357. if self.args[0].assumptions0.get("below_fermi"):
  358. return 0
  359. else:
  360. return 1
  361. elif not self.is_below_fermi:
  362. if self.args[0].assumptions0.get("above_fermi"):
  363. return 0
  364. else:
  365. return 1
  366. else:
  367. return 0
  368. @property
  369. def indices(self):
  370. return self.args[0:2]
  371. def _eval_rewrite_as_Piecewise(self, *args, **kwargs):
  372. i, j = args
  373. return Piecewise((0, Ne(i, j)), (1, True))