Repository: sympy/sympy

Issue:
Getting simplify to simplify \integral 0
I have the following expression:

```
\frac{1}{\sqrt{\pi}} \int_{0}^{\frac{x}{2 \sqrt{t}}} 0\, ds
```

Feeding this to simplify does nothing, even though this is a definite integral of 0, which should trivially be evaluated to zero. Why doesn't simplify simplify this to zero? Am I missing some assumptions?


Discussion:

Patch:
diff --git a/doc/src/modules/tensor/tensor.rst b/doc/src/modules/tensor/tensor.rst
index c05ad22af8..44b45be516 100644
--- a/doc/src/modules/tensor/tensor.rst
+++ b/doc/src/modules/tensor/tensor.rst
@@ -5,12 +5,25 @@ Tensor
 
 .. module:: sympy.tensor.tensor
 
+.. autoclass:: _TensorManager
+   :members:
+
 .. autoclass:: TensorIndexType
    :members:
 
 .. autoclass:: TensorIndex
    :members:
 
+.. autofunction:: tensor_indices
+
+.. autoclass:: TensorSymmetry
+   :members:
+
+.. autofunction:: tensorsymmetry
+
+.. autoclass:: TensorType
+   :members:
+
 .. autoclass:: TensorHead
    :members:
 
@@ -25,17 +38,8 @@ Tensor
 
 .. autofunction:: canon_bp
 
+.. autofunction:: tensor_mul
+
 .. autofunction:: riemann_cyclic_replace
 
 .. autofunction:: riemann_cyclic
-
-.. autoclass:: TensorSymmetry
-   :members:
-
-.. autofunction:: tensorsymmetry
-
-.. autoclass:: TensorType
-   :members:
-
-.. autoclass:: _TensorManager
-   :members:
diff --git a/sympy/simplify/simplify.py b/sympy/simplify/simplify.py
index 035ce9d6f5..7f055da80e 100644
--- a/sympy/simplify/simplify.py
+++ b/sympy/simplify/simplify.py
@@ -515,6 +515,10 @@ def simplify(expr, ratio=1.7, measure=count_ops, rational=False, inverse=False):
     kwargs = dict(ratio=ratio, measure=measure,
         rational=rational, inverse=inverse)
 
+    # no routine for Expr needs to check for is_zero
+    if isinstance(expr, Expr) and expr.is_zero and expr*0 is S.Zero:
+        return S.Zero
+
     _eval_simplify = getattr(expr, '_eval_simplify', None)
     if _eval_simplify is not None:
         return _eval_simplify(ratio=ratio, measure=measure, rational=rational, inverse=inverse)
diff --git a/sympy/tensor/tensor.py b/sympy/tensor/tensor.py
index a0c0df9480..258d823bd7 100644
--- a/sympy/tensor/tensor.py
+++ b/sympy/tensor/tensor.py
@@ -88,8 +88,13 @@ def from_indices(*indices):
         >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz)
         >>> _IndexStructure.from_indices(m0, m1, -m1, m3)
         _IndexStructure([(m0, 0), (m3, 3)], [(1, 2)], [Lorentz, Lorentz, Lorentz, Lorentz])
-        """
 
+        In case of many components the same indices have slightly different
+        indexes:
+
+        >>> _IndexStructure.from_indices(m0, m1, -m1, m3)
+        _IndexStructure([(m0, 0), (m3, 3)], [(1, 2)], [Lorentz, Lorentz, Lorentz, Lorentz])
+        """
         free, dum = _IndexStructure._free_dum_from_indices(*indices)
         index_types = [i.tensor_index_type for i in indices]
         indices = _IndexStructure._replace_dummy_names(indices, free, dum)
@@ -294,7 +299,7 @@ def indices_canon_args(self):
         """
         Returns ``(g, dummies, msym, v)``, the entries of ``canonicalize``
 
-        see ``canonicalize`` in ``tensor_can.py`` in combinatorics module
+        see ``canonicalize`` in ``tensor_can.py``
         """
         # to be called after sorted_components
         from sympy.combinatorics.permutations import _af_new
@@ -891,6 +896,7 @@ class TensorIndexType(Basic):
 
     metric : metric symmetry or metric object or ``None``
 
+
     dim : dimension, it can be a symbol or an integer or ``None``
 
     eps_dim : dimension of the epsilon tensor
@@ -909,7 +915,7 @@ class TensorIndexType(Basic):
     ``dim``
     ``eps_dim``
     ``dummy_fmt``
-    ``data`` : (deprecated) a property to add ``ndarray`` values, to work in a specified basis.
+    ``data`` : a property to add ``ndarray`` values, to work in a specified basis.
 
     Notes
     =====
@@ -1135,14 +1141,14 @@ def delete_tensmul_data(key):
 
 class TensorIndex(Basic):
     """
-    Represents a tensor index
+    Represents an abstract tensor index.
 
     Parameters
     ==========
 
     name : name of the index, or ``True`` if you want it to be automatically assigned
     tensortype : ``TensorIndexType`` of the index
-    is_up :  flag for contravariant index (is_up=True by default)
+    is_up :  flag for contravariant index
 
     Attributes
     ==========
@@ -1157,30 +1163,39 @@ class TensorIndex(Basic):
     Tensor indices are contracted with the Einstein summation convention.
 
     An index can be in contravariant or in covariant form; in the latter
-    case it is represented prepending a ``-`` to the index name. Adding
-    ``-`` to a covariant (is_up=False) index makes it contravariant.
+    case it is represented prepending a ``-`` to the index name.
 
     Dummy indices have a name with head given by ``tensortype._dummy_fmt``
 
-    Similar to ``symbols`` multiple contravariant indices can be created
-    at once using ``tensor_indices(s, typ)``, where ``s`` is a string
-    of names.
-
 
     Examples
     ========
 
-    >>> from sympy.tensor.tensor import TensorIndexType, TensorIndex, tensor_indices, tensorhead
+    >>> from sympy.tensor.tensor import TensorIndexType, TensorIndex, TensorSymmetry, TensorType, get_symmetric_group_sgs
     >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
-    >>> mu = TensorIndex('mu', Lorentz, is_up=False)
-    >>> nu, rho = tensor_indices('nu, rho', Lorentz)
-    >>> A = tensorhead('A', [Lorentz, Lorentz])
-    >>> A(mu, nu)
-    A(-mu, nu)
-    >>> A(-mu, -rho)
-    A(mu, -rho)
-    >>> A(mu, -mu)
-    A(-L_0, L_0)
+    >>> i = TensorIndex('i', Lorentz); i
+    i
+    >>> sym1 = TensorSymmetry(*get_symmetric_group_sgs(1))
+    >>> S1 = TensorType([Lorentz], sym1)
+    >>> A, B = S1('A,B')
+    >>> A(i)*B(-i)
+    A(L_0)*B(-L_0)
+
+    If you want the index name to be automatically assigned, just put ``True``
+    in the ``name`` field, it will be generated using the reserved character
+    ``_`` in front of its name, in order to avoid conflicts with possible
+    existing indices:
+
+    >>> i0 = TensorIndex(True, Lorentz)
+    >>> i0
+    _i0
+    >>> i1 = TensorIndex(True, Lorentz)
+    >>> i1
+    _i1
+    >>> A(i0)*B(-i1)
+    A(_i0)*B(-_i1)
+    >>> A(i0)*B(-i0)
+    A(L_0)*B(-L_0)
     """
     def __new__(cls, name, tensortype, is_up=True):
         if isinstance(name, string_types):
@@ -1264,9 +1279,7 @@ def tensor_indices(s, typ):
 
 class TensorSymmetry(Basic):
     """
-    Monoterm symmetry of a tensor (i.e. any symmetric or anti-symmetric
-    index permutation). For the relevant terminology see ``tensor_can.py``
-    section of the combinatorics module.
+    Monoterm symmetry of a tensor
 
     Parameters
     ==========
@@ -1284,8 +1297,8 @@ class TensorSymmetry(Basic):
     =====
 
     A tensor can have an arbitrary monoterm symmetry provided by its BSGS.
-    Multiterm symmetries, like the cyclic symmetry of the Riemann tensor
-    (Bianchi identity), are not covered
+    Multiterm symmetries, like the cyclic symmetry of the Riemann tensor,
+    are not covered.
 
     See Also
     ========
@@ -1506,7 +1519,7 @@ def tensorhead(name, typ, sym=None, comm=0):
     Parameters
     ==========
 
-    name : name or sequence of names (as in ``symbols``)
+    name : name or sequence of names (as in ``symbol``)
 
     typ :  index types
 
@@ -1526,7 +1539,8 @@ def tensorhead(name, typ, sym=None, comm=0):
     >>> A(a, -b)
     A(a, -b)
 
-    If no symmetry parameter is provided, assume there are no index symmetries:
+    If no symmetry parameter is provided, assume there are not index
+    symmetries:
 
     >>> B = tensorhead('B', [Lorentz, Lorentz])
     >>> B(a, -b)
@@ -1542,7 +1556,7 @@ def tensorhead(name, typ, sym=None, comm=0):
 
 
 class TensorHead(Basic):
-    """
+    r"""
     Tensor head of the tensor
 
     Parameters
@@ -1567,11 +1581,6 @@ class TensorHead(Basic):
     Notes
     =====
 
-    Similar to ``symbols`` multiple TensorHeads can be created using
-    ``tensorhead(s, typ, sym=None, comm=0)`` function, where ``s``
-    is the string of names and ``sym`` is the monoterm tensor symmetry
-    (see ``tensorsymmetry``).
-
     A ``TensorHead`` belongs to a commutation group, defined by a
     symbol on number ``comm`` (see ``_TensorManager.set_comm``);
     tensors in a commutation group have the same commutation properties;
@@ -1580,9 +1589,18 @@ class TensorHead(Basic):
     Examples
     ========
 
-    >>> from sympy.tensor.tensor import TensorIndexType, tensorhead, tensor_indices
-    >>> from sympy import diag
+    >>> from sympy.tensor.tensor import TensorIndexType, tensorhead, TensorType
     >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
+    >>> A = tensorhead('A', [Lorentz, Lorentz], [[1],[1]])
+
+    Examples with ndarray values, the components data assigned to the
+    ``TensorHead`` object are assumed to be in a fully-contravariant
+    representation. In case it is necessary to assign components data which
+    represents the values of a non-fully covariant tensor, see the other
+    examples.
+
+    >>> from sympy.tensor.tensor import tensor_indices, tensorhead
+    >>> from sympy import diag
     >>> i0, i1 = tensor_indices('i0:2', Lorentz)
 
     Specify a replacement dictionary to keep track of the arrays to use for
@@ -1808,6 +1826,9 @@ class TensExpr(Expr):
 
     A ``TensExpr`` can be a ``TensAdd`` or a ``TensMul``.
 
+    ``TensAdd`` objects are put in canonical form using the Butler-Portugal
+    algorithm for canonicalization under monoterm symmetries.
+
     ``TensMul`` objects are formed by products of component tensors,
     and include a coefficient, which is a SymPy expression.
 
@@ -2212,6 +2233,11 @@ class TensAdd(TensExpr, AssocOp):
     ``rank`` : rank of the tensor
     ``free_args`` : list of the free indices in sorted order
 
+    Notes
+    =====
+
+    Sum of more than one tensor are put automatically in canonical form.
+
     Examples
     ========
 
@@ -2403,7 +2429,7 @@ def __call__(self, *indices):
 
     def canon_bp(self):
         """
-        Canonicalize using the Butler-Portugal algorithm for canonicalization
+        canonicalize using the Butler-Portugal algorithm for canonicalization
         under monoterm symmetries.
         """
         expr = self.expand()
@@ -2589,16 +2615,6 @@ class Tensor(TensExpr):
     >>> A(mu, -mu)
     A(L_0, -L_0)
 
-    It is also possible to use symbols instead of inidices (appropriate indices
-    are then generated automatically).
-
-    >>> from sympy import Symbol
-    >>> x = Symbol('x')
-    >>> A(x, mu)
-    A(x, mu)
-    >>> A(x, -x)
-    A(L_0, -L_0)
-
     """
 
     is_commutative = False
@@ -3805,8 +3821,7 @@ def _extract_data(self, replacement_dict):
 
 def canon_bp(p):
     """
-    Butler-Portugal canonicalization. See ``tensor_can.py`` from the
-    combinatorics module for the details.
+    Butler-Portugal canonicalization
     """
     if isinstance(p, TensExpr):
         return p.canon_bp()


Test patch:
diff --git a/sympy/functions/special/tests/test_error_functions.py b/sympy/functions/special/tests/test_error_functions.py
index 08e6d7ee32..fea959759c 100644
--- a/sympy/functions/special/tests/test_error_functions.py
+++ b/sympy/functions/special/tests/test_error_functions.py
@@ -75,8 +75,6 @@ def test_erf():
 
     assert erf(w).as_real_imag() == (erf(w), 0)
     assert erf(w).as_real_imag(deep=False) == (erf(w), 0)
-    # issue 13575
-    assert erf(I).as_real_imag() == (0, -I*erf(I))
 
     raises(ArgumentIndexError, lambda: erf(x).fdiff(2))
 
diff --git a/sympy/series/tests/test_limits.py b/sympy/series/tests/test_limits.py
index fd45eb73cf..a7a8a688f1 100644
--- a/sympy/series/tests/test_limits.py
+++ b/sympy/series/tests/test_limits.py
@@ -4,8 +4,7 @@
     limit, exp, oo, log, sqrt, Limit, sin, floor, cos, ceiling,
     atan, gamma, Symbol, S, pi, Integral, Rational, I, EulerGamma,
     tan, cot, integrate, Sum, sign, Function, subfactorial, symbols,
-    binomial, simplify, frac, Float, sec, zoo, fresnelc, fresnels,
-    acos, erfi)
+    binomial, simplify, frac, Float, sec, zoo, fresnelc, fresnels)
 
 from sympy.calculus.util import AccumBounds
 from sympy.core.add import Add
@@ -541,13 +540,3 @@ def test_issue_14377():
 
 def test_issue_15984():
     assert limit((-x + log(exp(x) + 1))/x, x, oo, dir='-').doit() == 0
-
-
-def test_issue_13575():
-    result = limit(acos(erfi(x)), x, 1)
-    assert isinstance(result, Add)
-
-    re, im = result.evalf().as_real_imag()
-
-    assert abs(re) < 1e-12
-    assert abs(im - 1.08633774961570) < 1e-12
diff --git a/sympy/simplify/tests/test_simplify.py b/sympy/simplify/tests/test_simplify.py
index a8611ec385..28ff6378d8 100644
--- a/sympy/simplify/tests/test_simplify.py
+++ b/sympy/simplify/tests/test_simplify.py
@@ -806,3 +806,9 @@ def test_issue_15965():
     assert simplify(A + B) == anew + bnew
     assert simplify(A) == anew
     assert simplify(B) == bnew
+
+
+def test_issue_7971():
+    z = Integral(x, (x, 1, 1))
+    assert z != 0
+    assert simplify(z) is S.Zero