Repository: sympy/sympy

Issue:
sympy.vector  Implicit_Region result wrong for perimeter of unit circle  version 1.7.1
Using vector_integrate in the examples in the documentation:

from sympy.vector import ImplicitRegion
c2 = ImplicitRegion((x, y), (x - 2)**2 + (y - 1)**2 - 9)
vector_integrate(1, c2)
12*pi

should this not be 6*pi since the radius of the circle is 3?

It seems like you always get the correct answer multiplied by a factor of 2 for any given radius.  

Discussion:

Patch:
diff --git a/doc/src/modules/vector/vector_integration.rst b/doc/src/modules/vector/vector_integration.rst
index 9f5b732aee..2e1384636c 100644
--- a/doc/src/modules/vector/vector_integration.rst
+++ b/doc/src/modules/vector/vector_integration.rst
@@ -18,7 +18,7 @@ We define a coordinate system and make necesssary imports for examples.
 >>> from sympy.vector import CoordSys3D, ParametricRegion, ImplicitRegion, vector_integrate
 >>> from sympy.abc import r, x, y, z, theta, phi
 >>> C = CoordSys3D('C')
-    
+
 Calculation of Perimeter, Surface Area, and Volume
 ==================================================
 
@@ -30,16 +30,16 @@ We can also define a circle using its implicit equation.
 
 >>> implicit_circle = ImplicitRegion((x, y), x**2 + y**2 - 4)
 
-The perimeter of a figure is equal to the absolute value of its integral over a unit scalar field. 
+The perimeter of a figure is equal to the absolute value of its integral over a unit scalar field.
 
 >>> vector_integrate(1, param_circle)
 8*pi
 >>> vector_integrate(1, implicit_circle)
-8*pi
+4*pi
 
 Suppose a user wants to calculate the perimeter of a triangle. Determining the parametric representation of a triangle can be difficult. Instead, the user can use an object of :class:`~sympy.geometry.polygon.Polygon` class in the geometry module.
 
->>> from sympy.geometry import Point, Polygon 
+>>> from sympy.geometry import Point, Polygon
 >>> triangle = Polygon(Point(1, 2), (3, 5), (1,6))
 >>> vector_integrate(1, triangle)
 sqrt(5) + sqrt(13) + 4
@@ -70,8 +70,8 @@ pi*a**4*h/2
 
 Calculation of Flux
 ===================
-                   
-1. Consider a region of space in which there is a constant vectorfield 
+
+1. Consider a region of space in which there is a constant vectorfield
 :math:`E(x, y, z) = a\mathbf{\hat{k}}`.
 A  hemisphere of radius r  lies on the x-y plane. What is the flux of the field through the sphere?
 
@@ -81,15 +81,15 @@ A  hemisphere of radius r  lies on the x-y plane. What is the flux of the field
 >>> flux
 pi*a*r**2
 
-2. Consider  a  region  of  space  in  which  there  is  a  vector  field 
-:math:`E(x, y, z) = x^2 \mathbf{\hat{k}}` above the x-y plane, and a field 
+2. Consider  a  region  of  space  in  which  there  is  a  vector  field
+:math:`E(x, y, z) = x^2 \mathbf{\hat{k}}` above the x-y plane, and a field
 :math:`E(x, y, z) = y^2 \mathbf{\hat{k}}` below the x-y plane. What is the flux of that vector field through a cube of side length L with its center at the origin?”
 
 The field is parallel to the z-axis so only the top and bottom face of the box will contribute to flux.
 
 >>> L = symbols('L', positive=True)
->>> top_face = ParametricRegion((x, y, L/2), (x, -L/2, L/2), (y, -L/2, L/2)) 
->>> bottom_face = ParametricRegion((x, y, -L/2), (x, -L/2, L/2), (y, -L/2, L/2)) 
+>>> top_face = ParametricRegion((x, y, L/2), (x, -L/2, L/2), (y, -L/2, L/2))
+>>> bottom_face = ParametricRegion((x, y, -L/2), (x, -L/2, L/2), (y, -L/2, L/2))
 >>> flux = vector_integrate(C.x**2*C.k, top_face) + vector_integrate(C.y**2*C.k, bottom_face)
 >>> flux
 L**4/6
diff --git a/doc/src/tutorial/matrices.rst b/doc/src/tutorial/matrices.rst
index b48c8a5de0..1e76d90df8 100644
--- a/doc/src/tutorial/matrices.rst
+++ b/doc/src/tutorial/matrices.rst
@@ -54,20 +54,18 @@ expressions or as keys to dictionaries.  If you need an immutable version of
 Basic Operations
 ================
 
-Here are some basic operations on ``Matrix``.
-
 Shape
 -----
 
-To get the shape of a matrix, use :func:`~.shape()` function.
+Here are some basic operations on ``Matrix``.  To get the shape of a matrix
+use ``shape``
 
-    >>> from sympy import shape
     >>> M = Matrix([[1, 2, 3], [-2, 0, 4]])
     >>> M
     ⎡1   2  3⎤
     ⎢        ⎥
     ⎣-2  0  4⎦
-    >>> shape(M)
+    >>> M.shape
     (2, 3)
 
 Accessing Rows and Columns
diff --git a/sympy/__init__.py b/sympy/__init__.py
index d57e418e0c..2aa3c50230 100644
--- a/sympy/__init__.py
+++ b/sympy/__init__.py
@@ -216,7 +216,7 @@ def __sympy_debug():
         HankelTransform, InverseHankelTransform, singularityintegrate)
 
 from .tensor import (IndexedBase, Idx, Indexed, get_contraction_structure,
-        get_indices, shape, MutableDenseNDimArray, ImmutableDenseNDimArray,
+        get_indices, MutableDenseNDimArray, ImmutableDenseNDimArray,
         MutableSparseNDimArray, ImmutableSparseNDimArray, NDimArray,
         tensorproduct, tensorcontraction, tensordiagonal, derive_by_array,
         permutedims, Array, DenseNDimArray, SparseNDimArray)
@@ -456,7 +456,7 @@ def __sympy_debug():
 
     # sympy.tensor
     'IndexedBase', 'Idx', 'Indexed', 'get_contraction_structure',
-    'get_indices', 'shape', 'MutableDenseNDimArray', 'ImmutableDenseNDimArray',
+    'get_indices', 'MutableDenseNDimArray', 'ImmutableDenseNDimArray',
     'MutableSparseNDimArray', 'ImmutableSparseNDimArray', 'NDimArray',
     'tensorproduct', 'tensorcontraction', 'tensordiagonal', 'derive_by_array',
     'permutedims', 'Array', 'DenseNDimArray', 'SparseNDimArray',
diff --git a/sympy/integrals/integrals.py b/sympy/integrals/integrals.py
index 8ec4076673..0ea0a76813 100644
--- a/sympy/integrals/integrals.py
+++ b/sympy/integrals/integrals.py
@@ -26,7 +26,6 @@
 from sympy.series.order import Order
 from sympy.series.formal import FormalPowerSeries
 from sympy.simplify.fu import sincos_to_sum
-from sympy.tensor.functions import shape
 from sympy.utilities.misc import filldedent
 from sympy.utilities.exceptions import SymPyDeprecationWarning
 
@@ -1623,10 +1622,3 @@ def line_integrate(field, curve, vars):
 
     integral = Integral(Ft, curve.limits).doit(deep=False)
     return integral
-
-
-### Property function dispatching ###
-
-@shape.register(Integral)
-def _(expr):
-    return shape(expr.function)
diff --git a/sympy/matrices/__init__.py b/sympy/matrices/__init__.py
index c7cf351eba..a026b254c5 100644
--- a/sympy/matrices/__init__.py
+++ b/sympy/matrices/__init__.py
@@ -3,14 +3,14 @@
 Includes functions for fast creating matrices like zero, one/eye, random
 matrix, etc.
 """
-from .common import ShapeError, NonSquareMatrixError, MatrixKind
+from .common import ShapeError, NonSquareMatrixError
 from .dense import (
     GramSchmidt, casoratian, diag, eye, hessian, jordan_cell,
     list2numpy, matrix2numpy, matrix_multiply_elementwise, ones,
     randMatrix, rot_axis1, rot_axis2, rot_axis3, symarray, wronskian,
     zeros)
 from .dense import MutableDenseMatrix
-from .matrices import DeferredVector, MatrixBase
+from .matrices import DeferredVector, MatrixBase, MatrixKind
 
 Matrix = MutableMatrix = MutableDenseMatrix
 
@@ -33,7 +33,7 @@
 from .utilities import dotprodsimp
 
 __all__ = [
-    'ShapeError', 'NonSquareMatrixError', 'MatrixKind',
+    'ShapeError', 'NonSquareMatrixError',
 
     'GramSchmidt', 'casoratian', 'diag', 'eye', 'hessian', 'jordan_cell',
     'list2numpy', 'matrix2numpy', 'matrix_multiply_elementwise', 'ones',
@@ -42,7 +42,7 @@
 
     'MutableDenseMatrix',
 
-    'DeferredVector', 'MatrixBase',
+    'DeferredVector', 'MatrixBase', 'MatrixKind',
 
     'Matrix', 'MutableMatrix',
 
diff --git a/sympy/matrices/common.py b/sympy/matrices/common.py
index 739e08075b..ef31119908 100644
--- a/sympy/matrices/common.py
+++ b/sympy/matrices/common.py
@@ -15,7 +15,6 @@
 from sympy.core.basic import Atom
 from sympy.core.compatibility import as_int, is_sequence
 from sympy.core.decorators import call_highest_priority
-from sympy.core.kind import Kind, NumberKind
 from sympy.core.logic import fuzzy_and
 from sympy.core.singleton import S
 from sympy.core.symbol import Symbol
@@ -2990,69 +2989,6 @@ def __iter__(self): # supports numpy.matrix and numpy.array
         return iter(sympify(mat[r, c]) for r in range(self.rows) for c in range(cols))
 
 
-class MatrixKind(Kind):
-    """
-    Kind for all matrices in SymPy.
-
-    Basic class for this kind is ``MatrixBase`` and ``MatrixExpr``,
-    but any expression representing the matrix can have this.
-
-    Parameters
-    ==========
-
-    element_kind : Kind
-        Kind of the element. Default is :obj:NumberKind `<sympy.core.kind.NumberKind>`,
-        which means that the matrix contains only numbers.
-
-    Examples
-    ========
-
-    Any instance of matrix class has ``MatrixKind``.
-
-    >>> from sympy import MatrixSymbol
-    >>> A = MatrixSymbol('A', 2,2)
-    >>> A.kind
-    MatrixKind(NumberKind)
-
-    Although expression representing a matrix may be not instance of
-    matrix class, it will have ``MatrixKind`` as well.
-
-    >>> from sympy import Integral
-    >>> from sympy.matrices.expressions import MatrixExpr
-    >>> from sympy.abc import x
-    >>> intM = Integral(A, x)
-    >>> isinstance(intM, MatrixExpr)
-    False
-    >>> intM.kind
-    MatrixKind(NumberKind)
-
-    Use ``isinstance()`` to check for ``MatrixKind` without specifying
-    the element kind. Use ``is`` with specifying the element kind.
-
-    >>> from sympy import Matrix
-    >>> from sympy.matrices import MatrixKind
-    >>> from sympy.core.kind import NumberKind
-    >>> boolM = Matrix([True, False])
-    >>> isinstance(boolM.kind, MatrixKind)
-    True
-    >>> boolM.kind is MatrixKind(NumberKind)
-    False
-
-    See Also
-    ========
-
-    shape : Function to return the shape of objects with ``MatrixKind``.
-
-    """
-    def __new__(cls, element_kind=NumberKind):
-        obj = super().__new__(cls, element_kind)
-        obj.element_kind = element_kind
-        return obj
-
-    def __repr__(self):
-        return "MatrixKind(%s)" % self.element_kind
-
-
 def _matrixify(mat):
     """If `mat` is a Matrix or is matrix-like,
     return a Matrix or MatrixWrapper object.  Otherwise
diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py
index f7b4aeebf3..114ed549be 100644
--- a/sympy/matrices/matrices.py
+++ b/sympy/matrices/matrices.py
@@ -6,7 +6,6 @@
     Callable, NotIterable, as_int, is_sequence)
 from sympy.core.decorators import deprecated
 from sympy.core.expr import Expr
-from sympy.core.kind import _NumberKind, NumberKind, UndefinedKind
 from sympy.core.mul import Mul
 from sympy.core.power import Pow
 from sympy.core.singleton import S
@@ -20,13 +19,14 @@
 from sympy.printing import sstr
 from sympy.printing.defaults import Printable
 from sympy.simplify import simplify as _simplify
+from sympy.core.kind import Kind, NumberKind, _NumberKind
 from sympy.utilities.exceptions import SymPyDeprecationWarning
 from sympy.utilities.iterables import flatten
 from sympy.utilities.misc import filldedent
 
 from .common import (
     MatrixCommon, MatrixError, NonSquareMatrixError, NonInvertibleMatrixError,
-    ShapeError, MatrixKind)
+    ShapeError)
 
 from .utilities import _iszero, _is_zero_after_expand_mul
 
@@ -744,6 +744,46 @@ def permuteFwd(self, perm):
         return self.permute_rows(perm, direction='forward')
 
 
+class MatrixKind(Kind):
+    """
+    Kind for all matrices in SymPy.
+
+    Basic class for this kind is ``MatrixBase`` and ``MatrixExpr``,
+    but any expression representing the matrix can have this.
+
+    Parameters
+    ==========
+
+    element_kind : Kind
+        Kind of the element. Default is ``NumberKind``, which means that
+        the matrix contains only numbers.
+
+    Examples
+    ========
+
+    >>> from sympy import MatrixSymbol, Integral
+    >>> from sympy.abc import x
+    >>> A = MatrixSymbol('A', 2,2)
+    >>> A.kind
+    MatrixKind(NumberKind)
+    >>> Integral(A,x).kind
+    MatrixKind(NumberKind)
+
+    See Also
+    ========
+
+    sympy.tensor.ArrayKind : Kind for N-dimensional arrays.
+
+    """
+    def __new__(cls, element_kind=NumberKind):
+        obj = super().__new__(cls, element_kind)
+        obj.element_kind = element_kind
+        return obj
+
+    def __repr__(self):
+        return "MatrixKind(%s)" % self.element_kind
+
+
 @Mul._kind_dispatcher.register(_NumberKind, MatrixKind)
 def num_mat_mul(k1, k2):
     """
@@ -778,14 +818,7 @@ class MatrixBase(MatrixDeprecated,
     zero = S.Zero
     one = S.One
 
-    @property
-    def kind(self):
-        elem_kinds = set(e.kind for e in self._mat)
-        if len(elem_kinds) == 1:
-            elemkind, = elem_kinds
-        else:
-            elemkind = UndefinedKind
-        return MatrixKind(elemkind)
+    kind = MatrixKind()
 
     def __array__(self, dtype=object):
         from .dense import matrix2numpy
diff --git a/sympy/tensor/__init__.py b/sympy/tensor/__init__.py
index a832614b1d..bb266e79da 100644
--- a/sympy/tensor/__init__.py
+++ b/sympy/tensor/__init__.py
@@ -3,7 +3,6 @@
 """
 from .indexed import IndexedBase, Idx, Indexed
 from .index_methods import get_contraction_structure, get_indices
-from .functions import shape
 from .array import (MutableDenseNDimArray, ImmutableDenseNDimArray,
     MutableSparseNDimArray, ImmutableSparseNDimArray, NDimArray, tensorproduct,
     tensorcontraction, tensordiagonal, derive_by_array, permutedims, Array,
@@ -14,8 +13,6 @@
 
     'get_contraction_structure', 'get_indices',
 
-    'shape',
-
     'MutableDenseNDimArray', 'ImmutableDenseNDimArray',
     'MutableSparseNDimArray', 'ImmutableSparseNDimArray', 'NDimArray',
     'tensorproduct', 'tensorcontraction', 'tensordiagonal', 'derive_by_array', 'permutedims',
diff --git a/sympy/tensor/array/ndim_array.py b/sympy/tensor/array/ndim_array.py
index 69052cc161..aed0cbc977 100644
--- a/sympy/tensor/array/ndim_array.py
+++ b/sympy/tensor/array/ndim_array.py
@@ -3,7 +3,7 @@
 from sympy.core.expr import Expr
 from sympy.core.numbers import Integer
 from sympy.core.sympify import sympify
-from sympy.core.kind import Kind, NumberKind, UndefinedKind
+from sympy.core.kind import Kind, NumberKind
 from sympy.core.compatibility import SYMPY_INTS
 from sympy.printing.defaults import Printable
 
@@ -23,45 +23,20 @@ class ArrayKind(Kind):
     ==========
 
     element_kind : Kind
-        Kind of the element. Default is :obj:NumberKind `<sympy.core.kind.NumberKind>`,
-        which means that the array contains only numbers.
+        Kind of the element. Default is ``NumberKind``, which means that
+        the array contains only numbers.
 
     Examples
     ========
 
-    Any instance of array class has ``ArrayKind``.
-
     >>> from sympy import NDimArray
     >>> NDimArray([1,2,3]).kind
     ArrayKind(NumberKind)
 
-    Although expressions representing an array may be not instance of
-    array class, it will have ``ArrayKind`` as well.
-
-    >>> from sympy import Integral
-    >>> from sympy.tensor.array import NDimArray
-    >>> from sympy.abc import x
-    >>> intA = Integral(NDimArray([1,2,3]), x)
-    >>> isinstance(intA, NDimArray)
-    False
-    >>> intA.kind
-    ArrayKind(NumberKind)
-
-    Use ``isinstance()`` to check for ``ArrayKind` without specifying
-    the element kind. Use ``is`` with specifying the element kind.
-
-    >>> from sympy.tensor.array import ArrayKind
-    >>> from sympy.core.kind import NumberKind
-    >>> boolA = NDimArray([True, False])
-    >>> isinstance(boolA.kind, ArrayKind)
-    True
-    >>> boolA.kind is ArrayKind(NumberKind)
-    False
-
     See Also
     ========
 
-    shape : Function to return the shape of objects with ``MatrixKind``.
+    sympy.matrices.MatrixKind : Kind for matrices.
 
     """
     def __new__(cls, element_kind=NumberKind):
@@ -135,12 +110,7 @@ def __new__(cls, iterable, shape=None, **kwargs):
 
     @property
     def kind(self):
-        elem_kinds = set(e.kind for e in self._array)
-        if len(elem_kinds) == 1:
-            elemkind, = elem_kinds
-        else:
-            elemkind = UndefinedKind
-        return ArrayKind(elemkind)
+        return ArrayKind()
 
     def _parse_index(self, index):
         if isinstance(index, (SYMPY_INTS, Integer)):
diff --git a/sympy/tensor/functions.py b/sympy/tensor/functions.py
index 27cb4e8dec..869383f33c 100644
--- a/sympy/tensor/functions.py
+++ b/sympy/tensor/functions.py
@@ -1,5 +1,4 @@
 from collections.abc import Iterable
-from functools import singledispatch
 
 from sympy import Expr, S, Mul, sympify
 from sympy.core.parameters import global_parameters
@@ -62,89 +61,3 @@ def __getitem__(self, index):
             arg.__getitem__(tuple(next(index) for i in shp))
             for arg, shp in zip(self.args, self._get_args_shapes())
         )
-
-
-@singledispatch
-def shape(expr):
-    """
-    Return the shape of the *expr* as a tuple. *expr* should represent
-    suitable object such as matrix or array.
-
-    Parameters
-    ==========
-
-    expr : SymPy object having ``MatrixKind`` or ``ArrayKind``.
-
-    Raises
-    ======
-
-    NoShapeError : Raised when object with wrong kind is passed.
-
-    Examples
-    ========
-
-    This function returns the shape of any object representing matrix or array.
-
-    >>> from sympy import shape, Array, Matrix, Integral
-    >>> from sympy.abc import x
-    >>> A = Array([1, 2])
-    >>> shape(A)
-    (2,)
-    >>> shape(Integral(A, x))
-    (2,)
-    >>> M = Matrix([1, 2])
-    >>> shape(M)
-    (2, 1)
-    >>> shape(Integral(M, x))
-    (2, 1)
-
-    You can support new type by dispatching.
-
-    >>> from sympy import Expr
-    >>> class NewExpr(Expr):
-    ...     pass
-    >>> @shape.register(NewExpr)
-    ... def _(expr):
-    ...     return shape(expr.args[0])
-    >>> shape(NewExpr(M))
-    (2, 1)
-
-    If unsuitable expression is passed, ``NoShapeError()`` will be raised.
-
-    >>> shape(Integral(x, x))
-    Traceback (most recent call last):
-      ...
-    sympy.tensor.functions.NoShapeError: shape() called on non-array object: Integral(x, x)
-
-    Notes
-    =====
-
-    Array-like classes (such as ``Matrix`` or ``NDimArray``) has ``shape``
-    property which returns its shape, but it cannot be used for non-array
-    classes containing array. This function returns the shape of any
-    registered object representing array.
-
-    """
-    if hasattr(expr, "shape"):
-        return expr.shape
-    raise NoShapeError(
-        "%s does not have shape, or its type is not registered to shape()." % expr)
-
-
-class NoShapeError(Exception):
-    """
-    Raised when ``shape()`` is called on non-array object.
-
-    This error can be imported from ``sympy.tensor.functions``.
-
-    Examples
-    ========
-
-    >>> from sympy import shape
-    >>> from sympy.abc import x
-    >>> shape(x)
-    Traceback (most recent call last):
-      ...
-    sympy.tensor.functions.NoShapeError: shape() called on non-array object: x
-    """
-    pass
diff --git a/sympy/vector/integrals.py b/sympy/vector/integrals.py
index 6984883f52..66ec14a14f 100644
--- a/sympy/vector/integrals.py
+++ b/sympy/vector/integrals.py
@@ -174,7 +174,7 @@ def vector_integrate(field, *region):
     >>> from sympy.vector import ImplicitRegion
     >>> c2 = ImplicitRegion((x, y), (x - 2)**2 + (y - 1)**2 - 9)
     >>> vector_integrate(1, c2)
-    12*pi
+    6*pi
 
     Integral of fields with respect to base scalars:
 
diff --git a/sympy/vector/parametricregion.py b/sympy/vector/parametricregion.py
index c3cd6f0e0a..6c00e238d5 100644
--- a/sympy/vector/parametricregion.py
+++ b/sympy/vector/parametricregion.py
@@ -181,7 +181,7 @@ def _(obj, parameters=('t', 's')):
     for i in range(len(obj.variables) - 1):
         # Each parameter is replaced by its tangent to simplify intergation
         parameter = _symbol(parameters[i], real=True)
-        definition = [trigsimp(elem.subs(parameter, tan(parameter))) for elem in definition]
+        definition = [trigsimp(elem.subs(parameter, tan(parameter/2))) for elem in definition]
         bounds.append((parameter, 0, 2*pi),)
 
     definition = Tuple(*definition)


Test patch:
diff --git a/sympy/vector/tests/test_integrals.py b/sympy/vector/tests/test_integrals.py
index e5271a2111..6d283ea8ed 100644
--- a/sympy/vector/tests/test_integrals.py
+++ b/sympy/vector/tests/test_integrals.py
@@ -95,9 +95,9 @@ def test_vector_integrate():
     assert vector_integrate(C.i*C.y - C.z, point) == ParametricIntegral(C.y*C.i, ParametricRegion((2, 3)))
 
     c3 = ImplicitRegion((x, y), x**2 + y**2 - 4)
-    assert vector_integrate(45, c3) == 360*pi
+    assert vector_integrate(45, c3) == 180*pi
     c4 = ImplicitRegion((x, y), (x - 3)**2 + (y - 4)**2 - 9)
-    assert vector_integrate(1, c4) == 12*pi
+    assert vector_integrate(1, c4) == 6*pi
 
     pl = Plane(Point(1, 1, 1), Point(2, 3, 4), Point(2, 2, 2))
     raises(ValueError, lambda: vector_integrate(C.x*C.z*C.i + C.k, pl))