Imported Upstream version 1.18.3 upstream/1.18.3
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 00:51:13 +0000 (09:51 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 00:51:13 +0000 (09:51 +0900)
14 files changed:
doc/changelog/1.18.3-changelog.rst [new file with mode: 0644]
doc/source/release.rst
doc/source/release/1.18.2-notes.rst
doc/source/release/1.18.3-notes.rst [new file with mode: 0644]
numpy/core/src/multiarray/arraytypes.c.src
numpy/core/src/multiarray/convert_datatype.c
numpy/core/src/multiarray/getset.c
numpy/core/tests/test_longdouble.py
numpy/random/_generator.pyx
numpy/random/mtrand.pyx
numpy/random/tests/test_generator_mt19937.py
numpy/random/tests/test_random.py
pavement.py
setup.py

diff --git a/doc/changelog/1.18.3-changelog.rst b/doc/changelog/1.18.3-changelog.rst
new file mode 100644 (file)
index 0000000..6ed2d48
--- /dev/null
@@ -0,0 +1,24 @@
+
+Contributors
+============
+
+A total of 6 people contributed to this release.  People with a "+" by their
+names contributed a patch for the first time.
+
+* Charles Harris
+* Max Balandat +
+* @Mibu287 +
+* Pan Jan +
+* Sebastian Berg
+* @panpiort8 +
+
+Pull requests merged
+====================
+
+A total of 5 pull requests were merged for this release.
+
+* `#15916 <https://github.com/numpy/numpy/pull/15916>`__: BUG: Fix eigh and cholesky methods of numpy.random.multivariate_normal
+* `#15929 <https://github.com/numpy/numpy/pull/15929>`__: BUG,MAINT: Remove incorrect special case in string to number...
+* `#15930 <https://github.com/numpy/numpy/pull/15930>`__: BUG: Guarantee array is in valid state after memory error occurs...
+* `#15954 <https://github.com/numpy/numpy/pull/15954>`__: BUG: Check that `pvals` is 1D in `_generator.multinomial`.
+* `#16017 <https://github.com/numpy/numpy/pull/16017>`__: BUG: Alpha parameter must be 1D in `generator.dirichlet`
index d8726d5b85a46537100e00c1ce7b30f8bd3101d5..31bd819d66db225ad77cf97dac83b2aac097849a 100644 (file)
@@ -5,6 +5,7 @@ Release Notes
 .. toctree::
     :maxdepth: 3
 
+    1.18.3 <release/1.18.3-notes>
     1.18.2 <release/1.18.2-notes>
     1.18.1 <release/1.18.1-notes>
     1.18.0 <release/1.18.0-notes>
index 629449b193e18affc1144c5eb060632a9a8c2b0f..2681a907f48ba8ae16c5fabc8b753bacf9e8264d 100644 (file)
@@ -4,7 +4,7 @@
 NumPy 1.18.2 Release Notes
 ==========================
 
-This small elease contains a fix for a performance regression in numpy/random
+This small release contains a fix for a performance regression in numpy/random
 and several bug/maintenance updates.
 
 The Python versions supported in this release are 3.5-3.8. Downstream
diff --git a/doc/source/release/1.18.3-notes.rst b/doc/source/release/1.18.3-notes.rst
new file mode 100644 (file)
index 0000000..1ebad52
--- /dev/null
@@ -0,0 +1,45 @@
+.. currentmodule:: numpy
+
+==========================
+NumPy 1.18.3 Release Notes
+==========================
+
+This release contains various bug/regression fixes.
+
+The Python versions supported in this release are 3.5-3.8. Downstream
+developers should use Cython >= 0.29.15 for Python 3.8 support and OpenBLAS >=
+3.7 to avoid errors on the Skylake architecture.
+
+
+Highlights
+==========
+
+* Fix for the `method='eigh'` and `method='cholesky'` methods in
+  `numpy.random.multivariate_normal`. Those were producing samples from the
+  wrong distribution.
+
+
+Contributors
+============
+
+A total of 6 people contributed to this release.  People with a "+" by their
+names contributed a patch for the first time.
+
+* Charles Harris
+* Max Balandat +
+* @Mibu287 +
+* Pan Jan +
+* Sebastian Berg
+* @panpiort8 +
+
+
+Pull requests merged
+====================
+
+A total of 5 pull requests were merged for this release.
+
+* `#15916 <https://github.com/numpy/numpy/pull/15916>`__: BUG: Fix eigh and cholesky methods of numpy.random.multivariate_normal
+* `#15929 <https://github.com/numpy/numpy/pull/15929>`__: BUG,MAINT: Remove incorrect special case in string to number...
+* `#15930 <https://github.com/numpy/numpy/pull/15930>`__: BUG: Guarantee array is in valid state after memory error occurs...
+* `#15954 <https://github.com/numpy/numpy/pull/15954>`__: BUG: Check that `pvals` is 1D in `_generator.multinomial`.
+* `#16017 <https://github.com/numpy/numpy/pull/16017>`__: BUG: Alpha parameter must be 1D in `generator.dirichlet`
index 9e108e3e140aeffe41b4aab8fe1c9f678743aa3b..598ee8537cf2b2811c738df722ac52578fff333e 100644 (file)
@@ -1527,16 +1527,8 @@ OBJECT_to_@TOTYPE@(void *input, void *output, npy_intp n,
  * #oskip = 1*18,(PyArray_DESCR(aop)->elsize)*3,1*2,
  *          1*18,(PyArray_DESCR(aop)->elsize)*3,1*2,
  *          1*18,(PyArray_DESCR(aop)->elsize)*3,1*2#
- * #convert = 1*18, 0*3, 1*2,
- *            1*18, 0*3, 1*2,
- *            0*23#
- * #convstr = (Int*9, Long*2, Float*4, Complex*3, Tuple*3, Long*2)*3#
  */
 
-#if @convert@
-
-#define IS_@from@
-
 static void
 @from@_to_@to@(void *input, void *output, npy_intp n,
         void *vaip, void *aop)
@@ -1550,41 +1542,10 @@ static void
     int oskip = @oskip@;
 
     for (i = 0; i < n; i++, ip+=skip, op+=oskip) {
-        PyObject *new;
         PyObject *temp = PyArray_Scalar(ip, PyArray_DESCR(aip), (PyObject *)aip);
         if (temp == NULL) {
             return;
         }
-
-#if defined(NPY_PY3K) && defined(IS_STRING)
-        /* Work around some Python 3K */
-        new = PyUnicode_FromEncodedObject(temp, "ascii", "strict");
-        Py_DECREF(temp);
-        temp = new;
-        if (temp == NULL) {
-            return;
-        }
-#endif
-        /* convert from Python object to needed one */
-        {
-            PyObject *args;
-
-            /* call out to the Python builtin given by convstr */
-            args = Py_BuildValue("(N)", temp);
-#if defined(NPY_PY3K)
-#define PyInt_Type PyLong_Type
-#endif
-            new = Py@convstr@_Type.tp_new(&Py@convstr@_Type, args, NULL);
-#if defined(NPY_PY3K)
-#undef PyInt_Type
-#endif
-            Py_DECREF(args);
-            temp = new;
-            if (temp == NULL) {
-                return;
-            }
-        }
-
         if (@to@_setitem(temp, op, aop)) {
             Py_DECREF(temp);
             return;
@@ -1593,36 +1554,6 @@ static void
     }
 }
 
-#undef IS_@from@
-
-#else
-
-static void
-@from@_to_@to@(void *input, void *output, npy_intp n,
-        void *vaip, void *aop)
-{
-    @fromtyp@ *ip = input;
-    @totyp@ *op = output;
-    PyArrayObject *aip = vaip;
-
-    npy_intp i;
-    int skip = PyArray_DESCR(aip)->elsize;
-    int oskip = @oskip@;
-
-    for (i = 0; i < n; i++, ip+=skip, op+=oskip) {
-        PyObject *temp = PyArray_Scalar(ip, PyArray_DESCR(aip), (PyObject *)aip);
-        if (temp == NULL) {
-            return;
-        }
-        if (@to@_setitem(temp, op, aop)) {
-            Py_DECREF(temp);
-            return;
-        }
-        Py_DECREF(temp);
-    }
-}
-
-#endif
 
 /**end repeat**/
 
index 4326448dcce8c9c1bd30c53a58dbd4d096c04986..14025a5f91e675d629220fe55b595c6d3dcdd061 100644 (file)
@@ -212,13 +212,17 @@ PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype,
                 case NPY_HALF:
                 case NPY_FLOAT:
                 case NPY_DOUBLE:
-                case NPY_LONGDOUBLE:
                     size = 32;
                     break;
+                case NPY_LONGDOUBLE:
+                    size = 48;
+                    break;
                 case NPY_CFLOAT:
                 case NPY_CDOUBLE:
+                    size = 2 * 32;
+                    break;
                 case NPY_CLONGDOUBLE:
-                    size = 64;
+                    size = 2 * 48;
                     break;
                 case NPY_OBJECT:
                     size = 64;
index 6e5d480d03573076da59c2893e379e0f82230b49..b405ab0992fad67c690315b3486bb0a05248644a 100644 (file)
@@ -67,28 +67,34 @@ array_shape_set(PyArrayObject *self, PyObject *val)
         return -1;
     }
 
-    /* Free old dimensions and strides */
-    npy_free_cache_dim_array(self);
     nd = PyArray_NDIM(ret);
-    ((PyArrayObject_fields *)self)->nd = nd;
     if (nd > 0) {
         /* create new dimensions and strides */
-        ((PyArrayObject_fields *)self)->dimensions = npy_alloc_cache_dim(2 * nd);
-        if (PyArray_DIMS(self) == NULL) {
+        npy_intp *_dimensions = npy_alloc_cache_dim(2 * nd);
+        if (_dimensions == NULL) {
             Py_DECREF(ret);
-            PyErr_SetString(PyExc_MemoryError,"");
+            PyErr_NoMemory();
             return -1;
         }
-        ((PyArrayObject_fields *)self)->strides = PyArray_DIMS(self) + nd;
+        /* Free old dimensions and strides */
+        npy_free_cache_dim_array(self);
+        ((PyArrayObject_fields *)self)->nd = nd;
+        ((PyArrayObject_fields *)self)->dimensions = _dimensions; 
+        ((PyArrayObject_fields *)self)->strides = _dimensions + nd;
+
         if (nd) {
             memcpy(PyArray_DIMS(self), PyArray_DIMS(ret), nd*sizeof(npy_intp));
             memcpy(PyArray_STRIDES(self), PyArray_STRIDES(ret), nd*sizeof(npy_intp));
         }
     }
     else {
+        /* Free old dimensions and strides */
+        npy_free_cache_dim_array(self);        
+        ((PyArrayObject_fields *)self)->nd = 0;
         ((PyArrayObject_fields *)self)->dimensions = NULL;
         ((PyArrayObject_fields *)self)->strides = NULL;
     }
+
     Py_DECREF(ret);
     PyArray_UpdateFlags(self, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
     return 0;
index 2b6e1c5a290e6113038ec8b3a911e871a076ad02..0ba994860f8c127a0e03f0b18644953676b426b5 100644 (file)
@@ -39,22 +39,36 @@ def test_repr_roundtrip():
     assert_equal(np.longdouble(repr(o)), o, "repr was %s" % repr(o))
 
 
-def test_unicode():
-    np.longdouble(u"1.2")
+@pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l")
+def test_repr_roundtrip_bytes():
+    o = 1 + LD_INFO.eps
+    assert_equal(np.longdouble(repr(o).encode("ascii")), o)
 
 
-def test_string():
-    np.longdouble("1.2")
+@pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l")
+@pytest.mark.parametrize("strtype", (np.str_, np.bytes_, str, bytes))
+def test_array_and_stringlike_roundtrip(strtype):
+    """
+    Test that string representations of long-double roundtrip both
+    for array casting and scalar coercion, see also gh-15608.
+    """
+    o = 1 + LD_INFO.eps
 
+    if strtype in (np.bytes_, bytes):
+        o_str = strtype(repr(o).encode("ascii"))
+    else:
+        o_str = strtype(repr(o))
 
-def test_bytes():
-    np.longdouble(b"1.2")
+    # Test that `o` is correctly coerced from the string-like
+    assert o == np.longdouble(o_str)
 
+    # Test that arrays also roundtrip correctly:
+    o_strarr = np.asarray([o] * 3, dtype=strtype)
+    assert (o == o_strarr.astype(np.longdouble)).all()
 
-@pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l")
-def test_repr_roundtrip_bytes():
-    o = 1 + LD_INFO.eps
-    assert_equal(np.longdouble(repr(o).encode("ascii")), o)
+    # And array coercion and casting to string give the same as scalar repr:
+    assert (o_strarr == o_str).all()
+    assert (np.asarray([o] * 3).astype(strtype) == o_str).all()
 
 
 def test_bogus_string():
index 6a217e95419b1092a0e23e951395856345df0876..7bfa8e1c09cb4a33b764e0514f95f76a8e22becb 100644 (file)
@@ -3643,10 +3643,9 @@ cdef class Generator:
             # approximately zero or when the covariance is not positive-semidefinite
             _factor = u * np.sqrt(abs(s))
         else:
-            _factor = np.sqrt(s)[:, None] * vh
+            _factor = u * np.sqrt(s)
 
-        x = np.dot(x, _factor)
-        x += mean
+        x = mean + x @ _factor.T
         x.shape = tuple(final_shape)
         return x
 
@@ -3749,8 +3748,8 @@ cdef class Generator:
 
         d = len(pvals)
         on = <np.ndarray>np.PyArray_FROM_OTF(n, np.NPY_INT64, np.NPY_ALIGNED)
-        parr = <np.ndarray>np.PyArray_FROM_OTF(
-            pvals, np.NPY_DOUBLE, np.NPY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
+        parr = <np.ndarray>np.PyArray_FROMANY(
+            pvals, np.NPY_DOUBLE, 1, 1, np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
         pix = <double*>np.PyArray_DATA(parr)
         check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1)
         if kahan_sum(pix, d-1) > (1.0 + 1e-12):
@@ -4030,23 +4029,23 @@ cdef class Generator:
 
         Parameters
         ----------
-        alpha : array
-            Parameter of the distribution (k dimension for sample of
-            dimension k).
+        alpha : sequence of floats, length k
+            Parameter of the distribution (length ``k`` for sample of
+            length ``k``).
         size : int or tuple of ints, optional
-            Output shape.  If the given shape is, e.g., ``(m, n, k)``, then
+            Output shape.  If the given shape is, e.g., ``(m, n)``, then
             ``m * n * k`` samples are drawn.  Default is None, in which case a
-            single value is returned.
+            vector of length ``k`` is returned.
 
         Returns
         -------
         samples : ndarray,
-            The drawn samples, of shape (size, alpha.ndim).
+            The drawn samples, of shape ``(size, k)``.
 
         Raises
         -------
         ValueError
-            If any value in alpha is less than or equal to zero
+            If any value in ``alpha`` is less than or equal to zero
 
         Notes
         -----
@@ -4120,8 +4119,9 @@ cdef class Generator:
         cdef double acc, invacc
 
         k = len(alpha)
-        alpha_arr = <np.ndarray>np.PyArray_FROM_OTF(
-            alpha, np.NPY_DOUBLE, np.NPY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
+        alpha_arr = <np.ndarray>np.PyArray_FROMANY(
+            alpha, np.NPY_DOUBLE, 1, 1,
+            np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
         if np.any(np.less_equal(alpha_arr, 0)):
             raise ValueError('alpha <= 0')
         alpha_data = <double*>np.PyArray_DATA(alpha_arr)
index d4f7d2d965e3ae04114df4507af9d001c6ead28f..0d86d1a3c5d0c4ccb1bbee86ae5fefcc6f22f99b 100644 (file)
@@ -4191,8 +4191,8 @@ cdef class RandomState:
         cdef long ni
 
         d = len(pvals)
-        parr = <np.ndarray>np.PyArray_FROM_OTF(
-            pvals, np.NPY_DOUBLE, np.NPY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
+        parr = <np.ndarray>np.PyArray_FROMANY(
+            pvals, np.NPY_DOUBLE, 1, 1, np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
         pix = <double*>np.PyArray_DATA(parr)
         check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1)
         if kahan_sum(pix, d-1) > (1.0 + 1e-12):
@@ -4238,23 +4238,23 @@ cdef class RandomState:
 
         Parameters
         ----------
-        alpha : array
-            Parameter of the distribution (k dimension for sample of
-            dimension k).
+        alpha : sequence of floats, length k
+            Parameter of the distribution (length ``k`` for sample of
+            length ``k``).
         size : int or tuple of ints, optional
-            Output shape.  If the given shape is, e.g., ``(m, n, k)``, then
+            Output shape.  If the given shape is, e.g., ``(m, n)``, then
             ``m * n * k`` samples are drawn.  Default is None, in which case a
-            single value is returned.
+            vector of length ``k`` is returned.
 
         Returns
         -------
         samples : ndarray,
-            The drawn samples, of shape (size, alpha.ndim).
+            The drawn samples, of shape ``(size, k)``.
 
         Raises
         -------
         ValueError
-            If any value in alpha is less than or equal to zero
+            If any value in ``alpha`` is less than or equal to zero
 
         See Also
         --------
@@ -4332,8 +4332,9 @@ cdef class RandomState:
         cdef double  acc, invacc
 
         k = len(alpha)
-        alpha_arr = <np.ndarray>np.PyArray_FROM_OTF(
-            alpha, np.NPY_DOUBLE, np.NPY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
+        alpha_arr = <np.ndarray>np.PyArray_FROMANY(
+            alpha, np.NPY_DOUBLE, 1, 1,
+            np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS)
         if np.any(np.less_equal(alpha_arr, 0)):
             raise ValueError('alpha <= 0')
         alpha_data = <double*>np.PyArray_DATA(alpha_arr)
index d835f16bd7b2cf3226419ad5dd110ee57faa7df5..da2e76928632e51e4342cbd48a40be5ec1ed4530 100644 (file)
@@ -116,6 +116,12 @@ class TestMultinomial(object):
         contig = random.multinomial(100, pvals=np.ascontiguousarray(pvals))
         assert_array_equal(non_contig, contig)
 
+    def test_multidimensional_pvals(self):
+        assert_raises(ValueError, random.multinomial, 10, [[0, 1]])
+        assert_raises(ValueError, random.multinomial, 10, [[0], [1]])
+        assert_raises(ValueError, random.multinomial, 10, [[[0], [1]], [[1], [0]]])
+        assert_raises(ValueError, random.multinomial, 10, np.array([[0, 1], [1, 0]]))
+
 
 class TestMultivariateHypergeometric(object):
 
@@ -1044,6 +1050,12 @@ class TestRandomDist(object):
         alpha = np.array([5.4e-01, -1.0e-16])
         assert_raises(ValueError, random.dirichlet, alpha)
 
+        # gh-15876
+        assert_raises(ValueError, random.dirichlet, [[5, 1]])
+        assert_raises(ValueError, random.dirichlet, [[5], [1]])
+        assert_raises(ValueError, random.dirichlet, [[[5], [1]], [[1], [5]]])
+        assert_raises(ValueError, random.dirichlet, np.array([[5, 1], [1, 5]]))
+
     def test_dirichlet_alpha_non_contiguous(self):
         a = np.array([51.72840233779265162, -1.0, 39.74494232180943953])
         alpha = a[::2]
@@ -1243,6 +1255,17 @@ class TestRandomDist(object):
         assert_raises(ValueError, random.multivariate_normal, mean, cov,
                       check_valid='raise', method='eigh')
 
+        # check degenerate samples from singular covariance matrix
+        cov = [[1, 1], [1, 1]]
+        if method in ('svd', 'eigh'):
+            samples = random.multivariate_normal(mean, cov, size=(3, 2),
+                                                 method=method)
+            assert_array_almost_equal(samples[..., 0], samples[..., 1],
+                                      decimal=6)
+        else:
+            assert_raises(LinAlgError, random.multivariate_normal, mean, cov,
+                          method='cholesky')
+
         cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32)
         with suppress_warnings() as sup:
             random.multivariate_normal(mean, cov, method=method)
@@ -1260,6 +1283,19 @@ class TestRandomDist(object):
         assert_raises(ValueError, random.multivariate_normal,
                       mu, np.eye(3))
 
+    @pytest.mark.parametrize("method", ["svd", "eigh", "cholesky"])
+    def test_multivariate_normal_basic_stats(self, method):
+        random = Generator(MT19937(self.seed))
+        n_s = 1000
+        mean = np.array([1, 2])
+        cov = np.array([[2, 1], [1, 2]])
+        s = random.multivariate_normal(mean, cov, size=(n_s,), method=method)
+        s_center = s - mean
+        cov_emp = (s_center.T @ s_center) / (n_s - 1)
+        # these are pretty loose and are only designed to detect major errors
+        assert np.all(np.abs(s_center.mean(-2)) < 0.1)
+        assert np.all(np.abs(cov_emp - cov) < 0.2)
+
     def test_negative_binomial(self):
         random = Generator(MT19937(self.seed))
         actual = random.negative_binomial(n=100, p=.12345, size=(3, 2))
index 2e2ecedf894ecb2ac4149d7139c837dae5f3220f..3785405eb324c8e73124461211a35c3bbb664edd 100644 (file)
@@ -92,6 +92,12 @@ class TestMultinomial(object):
         assert_raises(TypeError, np.random.multinomial, 1, p,
                       float(1))
 
+    def test_multidimensional_pvals(self):
+        assert_raises(ValueError, np.random.multinomial, 10, [[0, 1]])
+        assert_raises(ValueError, np.random.multinomial, 10, [[0], [1]])
+        assert_raises(ValueError, np.random.multinomial, 10, [[[0], [1]], [[1], [0]]])
+        assert_raises(ValueError, np.random.multinomial, 10, np.array([[0, 1], [1, 0]]))
+
 
 class TestSetState(object):
     def setup(self):
@@ -559,6 +565,12 @@ class TestRandomDist(object):
         alpha = np.array([5.4e-01, -1.0e-16])
         assert_raises(ValueError, np.random.mtrand.dirichlet, alpha)
 
+        # gh-15876
+        assert_raises(ValueError, random.dirichlet, [[5, 1]])
+        assert_raises(ValueError, random.dirichlet, [[5], [1]])
+        assert_raises(ValueError, random.dirichlet, [[[5], [1]], [[1], [5]]])
+        assert_raises(ValueError, random.dirichlet, np.array([[5, 1], [1, 5]]))
+
     def test_exponential(self):
         np.random.seed(self.seed)
         actual = np.random.exponential(1.1234, size=(3, 2))
index 09c7dbba348c3a83cddecd36adf75060ce7a3f05..88d1d767c954ce47308357ef669876a5eb10ecab 100644 (file)
@@ -41,7 +41,7 @@ from paver.easy import Bunch, options, task, sh
 #-----------------------------------
 
 # Path to the release notes
-RELEASE_NOTES = 'doc/source/release/1.18.2-notes.rst'
+RELEASE_NOTES = 'doc/source/release/1.18.3-notes.rst'
 
 
 #-------------------------------------------------------
index d7f807b71904e31720effbc82b5e5bb738af03c7..3309fa0975cf6119186425de24d2407ddb86828e 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -58,7 +58,7 @@ Operating System :: MacOS
 
 MAJOR               = 1
 MINOR               = 18
-MICRO               = 2
+MICRO               = 3
 ISRELEASED          = True
 VERSION             = '%d.%d.%d' % (MAJOR, MINOR, MICRO)