123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- 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
- @classmethod
- 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
- @classmethod
- 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 i != min(i, j, key=default_sort_key):
- if delta_range:
- return cls(j, i, delta_range)
- else:
- return cls(j, i)
- @property
- 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
- @property
- 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
- @property
- 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
- @property
- 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
- @property
- 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
- @property
- 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
- @property
- 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]
- @property
- 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
- @property
- 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))
|