Spaces:
Running
Running
from math import prod | |
from sympy.core import S, Integer | |
from sympy.core.function import Function | |
from sympy.core.logic import fuzzy_not | |
from sympy.core.relational import Ne | |
from sympy.core.sorting import default_sort_key | |
from sympy.external.gmpy import SYMPY_INTS | |
from sympy.functions.combinatorial.factorials import factorial | |
from sympy.functions.elementary.piecewise import Piecewise | |
from sympy.utilities.iterables import has_dups | |
############################################################################### | |
###################### Kronecker Delta, Levi-Civita etc. ###################### | |
############################################################################### | |
def Eijk(*args, **kwargs): | |
""" | |
Represent the Levi-Civita symbol. | |
This is a compatibility wrapper to ``LeviCivita()``. | |
See Also | |
======== | |
LeviCivita | |
""" | |
return LeviCivita(*args, **kwargs) | |
def eval_levicivita(*args): | |
"""Evaluate Levi-Civita symbol.""" | |
n = len(args) | |
return prod( | |
prod(args[j] - args[i] for j in range(i + 1, n)) | |
/ factorial(i) for i in range(n)) | |
# converting factorial(i) to int is slightly faster | |
class LeviCivita(Function): | |
""" | |
Represent the Levi-Civita symbol. | |
Explanation | |
=========== | |
For even permutations of indices it returns 1, for odd permutations -1, and | |
for everything else (a repeated index) it returns 0. | |
Thus it represents an alternating pseudotensor. | |
Examples | |
======== | |
>>> from sympy import LeviCivita | |
>>> from sympy.abc import i, j, k | |
>>> LeviCivita(1, 2, 3) | |
1 | |
>>> LeviCivita(1, 3, 2) | |
-1 | |
>>> LeviCivita(1, 2, 2) | |
0 | |
>>> LeviCivita(i, j, k) | |
LeviCivita(i, j, k) | |
>>> LeviCivita(i, j, i) | |
0 | |
See Also | |
======== | |
Eijk | |
""" | |
is_integer = True | |
def eval(cls, *args): | |
if all(isinstance(a, (SYMPY_INTS, Integer)) for a in args): | |
return eval_levicivita(*args) | |
if has_dups(args): | |
return S.Zero | |
def doit(self, **hints): | |
return eval_levicivita(*self.args) | |
class KroneckerDelta(Function): | |
""" | |
The discrete, or Kronecker, delta function. | |
Explanation | |
=========== | |
A function that takes in two integers $i$ and $j$. It returns $0$ if $i$ | |
and $j$ are not equal, or it returns $1$ if $i$ and $j$ are equal. | |
Examples | |
======== | |
An example with integer indices: | |
>>> from sympy import KroneckerDelta | |
>>> KroneckerDelta(1, 2) | |
0 | |
>>> KroneckerDelta(3, 3) | |
1 | |
Symbolic indices: | |
>>> from sympy.abc import i, j, k | |
>>> KroneckerDelta(i, j) | |
KroneckerDelta(i, j) | |
>>> KroneckerDelta(i, i) | |
1 | |
>>> KroneckerDelta(i, i + 1) | |
0 | |
>>> KroneckerDelta(i, i + 1 + k) | |
KroneckerDelta(i, i + k + 1) | |
Parameters | |
========== | |
i : Number, Symbol | |
The first index of the delta function. | |
j : Number, Symbol | |
The second index of the delta function. | |
See Also | |
======== | |
eval | |
DiracDelta | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Kronecker_delta | |
""" | |
is_integer = True | |
def eval(cls, i, j, delta_range=None): | |
""" | |
Evaluates the discrete delta function. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta | |
>>> from sympy.abc import i, j, k | |
>>> KroneckerDelta(i, j) | |
KroneckerDelta(i, j) | |
>>> KroneckerDelta(i, i) | |
1 | |
>>> KroneckerDelta(i, i + 1) | |
0 | |
>>> KroneckerDelta(i, i + 1 + k) | |
KroneckerDelta(i, i + k + 1) | |
# indirect doctest | |
""" | |
if delta_range is not None: | |
dinf, dsup = delta_range | |
if (dinf - i > 0) == True: | |
return S.Zero | |
if (dinf - j > 0) == True: | |
return S.Zero | |
if (dsup - i < 0) == True: | |
return S.Zero | |
if (dsup - j < 0) == True: | |
return S.Zero | |
diff = i - j | |
if diff.is_zero: | |
return S.One | |
elif fuzzy_not(diff.is_zero): | |
return S.Zero | |
if i.assumptions0.get("below_fermi") and \ | |
j.assumptions0.get("above_fermi"): | |
return S.Zero | |
if j.assumptions0.get("below_fermi") and \ | |
i.assumptions0.get("above_fermi"): | |
return S.Zero | |
# to make KroneckerDelta canonical | |
# following lines will check if inputs are in order | |
# if not, will return KroneckerDelta with correct order | |
if default_sort_key(j) < default_sort_key(i): | |
if delta_range: | |
return cls(j, i, delta_range) | |
else: | |
return cls(j, i) | |
def delta_range(self): | |
if len(self.args) > 2: | |
return self.args[2] | |
def _eval_power(self, expt): | |
if expt.is_positive: | |
return self | |
if expt.is_negative and expt is not S.NegativeOne: | |
return 1/self | |
def is_above_fermi(self): | |
""" | |
True if Delta can be non-zero above fermi. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> q = Symbol('q') | |
>>> KroneckerDelta(p, a).is_above_fermi | |
True | |
>>> KroneckerDelta(p, i).is_above_fermi | |
False | |
>>> KroneckerDelta(p, q).is_above_fermi | |
True | |
See Also | |
======== | |
is_below_fermi, is_only_below_fermi, is_only_above_fermi | |
""" | |
if self.args[0].assumptions0.get("below_fermi"): | |
return False | |
if self.args[1].assumptions0.get("below_fermi"): | |
return False | |
return True | |
def is_below_fermi(self): | |
""" | |
True if Delta can be non-zero below fermi. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> q = Symbol('q') | |
>>> KroneckerDelta(p, a).is_below_fermi | |
False | |
>>> KroneckerDelta(p, i).is_below_fermi | |
True | |
>>> KroneckerDelta(p, q).is_below_fermi | |
True | |
See Also | |
======== | |
is_above_fermi, is_only_above_fermi, is_only_below_fermi | |
""" | |
if self.args[0].assumptions0.get("above_fermi"): | |
return False | |
if self.args[1].assumptions0.get("above_fermi"): | |
return False | |
return True | |
def is_only_above_fermi(self): | |
""" | |
True if Delta is restricted to above fermi. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> q = Symbol('q') | |
>>> KroneckerDelta(p, a).is_only_above_fermi | |
True | |
>>> KroneckerDelta(p, q).is_only_above_fermi | |
False | |
>>> KroneckerDelta(p, i).is_only_above_fermi | |
False | |
See Also | |
======== | |
is_above_fermi, is_below_fermi, is_only_below_fermi | |
""" | |
return ( self.args[0].assumptions0.get("above_fermi") | |
or | |
self.args[1].assumptions0.get("above_fermi") | |
) or False | |
def is_only_below_fermi(self): | |
""" | |
True if Delta is restricted to below fermi. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> q = Symbol('q') | |
>>> KroneckerDelta(p, i).is_only_below_fermi | |
True | |
>>> KroneckerDelta(p, q).is_only_below_fermi | |
False | |
>>> KroneckerDelta(p, a).is_only_below_fermi | |
False | |
See Also | |
======== | |
is_above_fermi, is_below_fermi, is_only_above_fermi | |
""" | |
return ( self.args[0].assumptions0.get("below_fermi") | |
or | |
self.args[1].assumptions0.get("below_fermi") | |
) or False | |
def indices_contain_equal_information(self): | |
""" | |
Returns True if indices are either both above or below fermi. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> q = Symbol('q') | |
>>> KroneckerDelta(p, q).indices_contain_equal_information | |
True | |
>>> KroneckerDelta(p, q+1).indices_contain_equal_information | |
True | |
>>> KroneckerDelta(i, p).indices_contain_equal_information | |
False | |
""" | |
if (self.args[0].assumptions0.get("below_fermi") and | |
self.args[1].assumptions0.get("below_fermi")): | |
return True | |
if (self.args[0].assumptions0.get("above_fermi") | |
and self.args[1].assumptions0.get("above_fermi")): | |
return True | |
# if both indices are general we are True, else false | |
return self.is_below_fermi and self.is_above_fermi | |
def preferred_index(self): | |
""" | |
Returns the index which is preferred to keep in the final expression. | |
Explanation | |
=========== | |
The preferred index is the index with more information regarding fermi | |
level. If indices contain the same information, 'a' is preferred before | |
'b'. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> j = Symbol('j', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> KroneckerDelta(p, i).preferred_index | |
i | |
>>> KroneckerDelta(p, a).preferred_index | |
a | |
>>> KroneckerDelta(i, j).preferred_index | |
i | |
See Also | |
======== | |
killable_index | |
""" | |
if self._get_preferred_index(): | |
return self.args[1] | |
else: | |
return self.args[0] | |
def killable_index(self): | |
""" | |
Returns the index which is preferred to substitute in the final | |
expression. | |
Explanation | |
=========== | |
The index to substitute is the index with less information regarding | |
fermi level. If indices contain the same information, 'a' is preferred | |
before 'b'. | |
Examples | |
======== | |
>>> from sympy import KroneckerDelta, Symbol | |
>>> a = Symbol('a', above_fermi=True) | |
>>> i = Symbol('i', below_fermi=True) | |
>>> j = Symbol('j', below_fermi=True) | |
>>> p = Symbol('p') | |
>>> KroneckerDelta(p, i).killable_index | |
p | |
>>> KroneckerDelta(p, a).killable_index | |
p | |
>>> KroneckerDelta(i, j).killable_index | |
j | |
See Also | |
======== | |
preferred_index | |
""" | |
if self._get_preferred_index(): | |
return self.args[0] | |
else: | |
return self.args[1] | |
def _get_preferred_index(self): | |
""" | |
Returns the index which is preferred to keep in the final expression. | |
The preferred index is the index with more information regarding fermi | |
level. If indices contain the same information, index 0 is returned. | |
""" | |
if not self.is_above_fermi: | |
if self.args[0].assumptions0.get("below_fermi"): | |
return 0 | |
else: | |
return 1 | |
elif not self.is_below_fermi: | |
if self.args[0].assumptions0.get("above_fermi"): | |
return 0 | |
else: | |
return 1 | |
else: | |
return 0 | |
def indices(self): | |
return self.args[0:2] | |
def _eval_rewrite_as_Piecewise(self, *args, **kwargs): | |
i, j = args | |
return Piecewise((0, Ne(i, j)), (1, True)) | |