From 575beed582f9288d83a403f4f578731f172f7f5f Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 11 Aug 2021 14:35:25 +0200 Subject: Add test for sphere and torus Fix numerical approximations inconsistencies with dim fraction exponent when generating points as grid on torus Add notes in doc regarding the torus versions use cases --- src/python/test/test_datasets_generators.py | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 src/python/test/test_datasets_generators.py (limited to 'src/python/test') diff --git a/src/python/test/test_datasets_generators.py b/src/python/test/test_datasets_generators.py new file mode 100755 index 00000000..656c30ee --- /dev/null +++ b/src/python/test/test_datasets_generators.py @@ -0,0 +1,40 @@ +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Hind Montassif + + Copyright (C) 2021 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification +""" + +from gudhi.datasets.generators import points +from gudhi.datasets.generators import _points + +import pytest + +def test_sphere(): + assert _points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'random').shape == (10, 2) + + with pytest.raises(ValueError): + _points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'other') + +def test_torus(): + assert _points.torus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) + assert _points.torus(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) + + assert _points.torus(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) + assert _points.torus(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) + + with pytest.raises(ValueError): + _points.torus(n_samples = 10, dim = 4, sample = 'other') + +def test_torus_full_python(): + assert points.torus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) + assert points.torus(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) + + assert points.torus(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) + assert points.torus(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) + + with pytest.raises(ValueError): + points.torus(n_samples = 10, dim = 4, sample = 'other') -- cgit v1.2.3 From 145fcba2de5f174b8fcdeab5ac1997978ffcdc0d Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 8 Sep 2021 18:01:11 +0200 Subject: Set the warning filter to "always" Add test for dtm overflow warning --- src/python/gudhi/point_cloud/knn.py | 6 ++++++ src/python/test/test_dtm.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'src/python/test') diff --git a/src/python/gudhi/point_cloud/knn.py b/src/python/gudhi/point_cloud/knn.py index dec5f88f..0724ce94 100644 --- a/src/python/gudhi/point_cloud/knn.py +++ b/src/python/gudhi/point_cloud/knn.py @@ -259,9 +259,11 @@ class KNearestNeighbors: neighbors, distances = self.graph.knn_query(X, k, num_threads=self.params["num_threads"]) if numpy.any(numpy.isnan(distances)): import warnings + warnings.simplefilter("always") warnings.warn("NaN value encountered while computing 'distances'", RuntimeWarning) if numpy.any(numpy.isinf(distances)): import warnings + warnings.simplefilter("always") warnings.warn("Overflow value encountered while computing 'distances'", RuntimeWarning) # The k nearest neighbors are always sorted. I couldn't find it in the doc, but the code calls searchKnn, # which returns a priority_queue, and then fills the return array backwards with top/pop on the queue. @@ -298,9 +300,11 @@ class KNearestNeighbors: distances, neighbors = mat.Kmin_argKmin(k, dim=1) if torch.isnan(distances).any(): import warnings + warnings.simplefilter("always") warnings.warn("NaN value encountered while computing 'distances'", RuntimeWarning) if torch.isinf(distances).any(): import warnings + warnings.simplefilter("always") warnings.warn("Overflow encountered while computing 'distances'", RuntimeWarning) if p != numpy.inf: distances = distances ** (1.0 / p) @@ -312,9 +316,11 @@ class KNearestNeighbors: distances = mat.Kmin(k, dim=1) if torch.isnan(distances).any(): import warnings + warnings.simplefilter("always") warnings.warn("NaN value encountered while computing 'distances'", RuntimeWarning) if torch.isinf(distances).any(): import warnings + warnings.simplefilter("always") warnings.warn("Overflow encountered while computing 'distances'", RuntimeWarning) if p != numpy.inf: distances = distances ** (1.0 / p) diff --git a/src/python/test/test_dtm.py b/src/python/test/test_dtm.py index 0a52279e..c29471cf 100755 --- a/src/python/test/test_dtm.py +++ b/src/python/test/test_dtm.py @@ -13,6 +13,7 @@ import numpy import pytest import torch import math +import warnings def test_dtm_compare_euclidean(): @@ -87,3 +88,16 @@ def test_density(): assert density == pytest.approx(expected) density = DTMDensity(weights=[0.5, 0.5], metric="neighbors", dim=1).fit_transform(distances) assert density == pytest.approx(expected) + +def test_dtm_overflow_warnings(): + pts = numpy.array([[10., 100000000000000000000000000000.], [1000., 100000000000000000000000000.]]) + impl_warn = ["keops", "hnsw"] + + with warnings.catch_warnings(record=True) as w: + for impl in impl_warn: + dtm = DistanceToMeasure(2, q=10000, implementation=impl) + r = dtm.fit_transform(pts) + assert len(w) == 2 + for i in range(len(w)): + assert issubclass(w[i].category, RuntimeWarning) + assert "Overflow" in str(w[i].message) -- cgit v1.2.3 From e23ca84fadcc2c65fd8cf2d141be804bf18b2fd6 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 22 Sep 2021 15:20:03 +0200 Subject: Rename function of torus cpp version and import it with sphere in points Change documentation accordingly --- src/python/doc/datasets_generators.rst | 28 +++++++++++++------------ src/python/gudhi/datasets/generators/_points.cc | 9 +++++--- src/python/gudhi/datasets/generators/points.py | 3 +++ src/python/test/test_datasets_generators.py | 15 +++++++------ 4 files changed, 31 insertions(+), 24 deletions(-) (limited to 'src/python/test') diff --git a/src/python/doc/datasets_generators.rst b/src/python/doc/datasets_generators.rst index e63dde82..c0bbb973 100644 --- a/src/python/doc/datasets_generators.rst +++ b/src/python/doc/datasets_generators.rst @@ -13,10 +13,12 @@ We provide the generation of different customizable datasets to use as inputs fo Points generators ------------------ +The module **points** enables the generation of random points on a sphere, random points on a torus and as a grid. + Points on sphere ^^^^^^^^^^^^^^^^ -The module **_points** enables the generation of random i.i.d. points uniformly on a (d-1)-sphere in :math:`R^d`. +The function **sphere** enables the generation of random i.i.d. points uniformly on a (d-1)-sphere in :math:`R^d`. The user should provide the number of points to be generated on the sphere :code:`n_samples` and the ambient dimension :code:`ambient_dim`. The :code:`radius` of sphere is optional and is equal to **1** by default. Only random points generation is currently available. @@ -28,28 +30,28 @@ Example .. code-block:: python - from gudhi.datasets.generators import _points + from gudhi.datasets.generators import points from gudhi import AlphaComplex # Generate 50 points on a sphere in R^2 - gen_points = _points.sphere(n_samples = 50, ambient_dim = 2, radius = 1, sample = "random") + gen_points = points.sphere(n_samples = 50, ambient_dim = 2, radius = 1, sample = "random") # Create an alpha complex from the generated points alpha_complex = AlphaComplex(points = gen_points) -.. autofunction:: gudhi.datasets.generators._points.sphere +.. autofunction:: gudhi.datasets.generators.points.sphere Points on torus ^^^^^^^^^^^^^^^^ You can also generate points on a torus. -Two modules are available and give the same output: the first one depends on **CGAL** and the second does not and consists of full python code. +Two functions are available and give the same output: the first one depends on **CGAL** and the second does not and consists of full python code. On another hand, two sample types are provided : you can either generate i.i.d. points on a d-torus in :math:`R^{2d}` *randomly* or on a *grid*. -First module : **_points** -"""""""""""""""""""""""""" +First function : **ctorus** +""""""""""""""""""""""""""" The user should provide the number of points to be generated on the torus :code:`n_samples`, and the dimension :code:`dim` of the torus on which points would be generated in :math:`R^{2dim}`. The :code:`sample` argument is optional and is set to **'random'** by default. @@ -67,18 +69,18 @@ Example """"""" .. code-block:: python - from gudhi.datasets.generators import _points + from gudhi.datasets.generators import points # Generate 50 points randomly on a torus in R^6 - gen_points = _points.torus(n_samples = 50, dim = 3) + gen_points = points.ctorus(n_samples = 50, dim = 3) # Generate 27 points on a torus as a grid in R^6 - gen_points = _points.torus(n_samples = 50, dim = 3, sample = 'grid') + gen_points = points.ctorus(n_samples = 50, dim = 3, sample = 'grid') -.. autofunction:: gudhi.datasets.generators._points.torus +.. autofunction:: gudhi.datasets.generators.points.ctorus -Second module : **points** -"""""""""""""""""""""""""" +Second function : **torus** +""""""""""""""""""""""""""" The user should provide the number of points to be generated on the torus :code:`n_samples` and the dimension :code:`dim` of the torus on which points would be generated in :math:`R^{2dim}`. The :code:`sample` argument is optional and is set to **'random'** by default. diff --git a/src/python/gudhi/datasets/generators/_points.cc b/src/python/gudhi/datasets/generators/_points.cc index 3d38ff90..536fa949 100644 --- a/src/python/gudhi/datasets/generators/_points.cc +++ b/src/python/gudhi/datasets/generators/_points.cc @@ -96,10 +96,10 @@ PYBIND11_MODULE(_points, m) { :returns: the generated points on a sphere. )pbdoc"); - m.def("torus", &generate_points_on_torus, + m.def("ctorus", &generate_points_on_torus, py::arg("n_samples"), py::arg("dim"), py::arg("sample") = "random", R"pbdoc( - Generate random i.i.d. points on a d-torus in R^2d + Generate random i.i.d. points on a d-torus in R^2d or as a grid :param n_samples: The number of points to be generated. :type n_samples: integer @@ -107,7 +107,10 @@ PYBIND11_MODULE(_points, m) { :type dim: integer :param sample: The sample type. Available values are: `"random"` and `"grid"`. Default value is `"random"`. :type sample: string - :rtype: numpy array of float + :rtype: numpy array of float. + The shape of returned numpy array is : + if sample is 'random' : (n_samples, 2*dim). + if sample is 'grid' : ([n_samples**(1./dim)]**dim, 2*dim). :returns: the generated points on a torus. )pbdoc"); } diff --git a/src/python/gudhi/datasets/generators/points.py b/src/python/gudhi/datasets/generators/points.py index daada486..1995f769 100644 --- a/src/python/gudhi/datasets/generators/points.py +++ b/src/python/gudhi/datasets/generators/points.py @@ -10,6 +10,9 @@ import numpy as np import itertools +from ._points import ctorus +from ._points import sphere + def _generate_random_points_on_torus(n_samples, dim): # Generate random angles of size n_samples*dim diff --git a/src/python/test/test_datasets_generators.py b/src/python/test/test_datasets_generators.py index 656c30ee..4c087c57 100755 --- a/src/python/test/test_datasets_generators.py +++ b/src/python/test/test_datasets_generators.py @@ -9,25 +9,24 @@ """ from gudhi.datasets.generators import points -from gudhi.datasets.generators import _points import pytest def test_sphere(): - assert _points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'random').shape == (10, 2) + assert points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'random').shape == (10, 2) with pytest.raises(ValueError): - _points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'other') + points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'other') def test_torus(): - assert _points.torus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) - assert _points.torus(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) + assert points.ctorus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) + assert points.ctorus(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) - assert _points.torus(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) - assert _points.torus(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) + assert points.ctorus(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) + assert points.ctorus(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) with pytest.raises(ValueError): - _points.torus(n_samples = 10, dim = 4, sample = 'other') + points.ctorus(n_samples = 10, dim = 4, sample = 'other') def test_torus_full_python(): assert points.torus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) -- cgit v1.2.3 From f461f050ee8bad509814b4851ab7ae8f43502962 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Fri, 8 Oct 2021 11:18:46 +0200 Subject: Add warnings in dtm.py for DistanceToMeasure and DTMDensity Add DTMDensity warning test --- src/python/gudhi/point_cloud/dtm.py | 11 +++++++++++ src/python/test/test_dtm.py | 10 +++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/python/test') diff --git a/src/python/gudhi/point_cloud/dtm.py b/src/python/gudhi/point_cloud/dtm.py index 55ac58e6..96a9e7bf 100644 --- a/src/python/gudhi/point_cloud/dtm.py +++ b/src/python/gudhi/point_cloud/dtm.py @@ -9,6 +9,7 @@ from .knn import KNearestNeighbors import numpy as np +import warnings __author__ = "Marc Glisse" __copyright__ = "Copyright (C) 2020 Inria" @@ -66,6 +67,11 @@ class DistanceToMeasure: distances = distances ** self.q dtm = distances.sum(-1) / self.k dtm = dtm ** (1.0 / self.q) + with warnings.catch_warnings(): + import torch + if isinstance(dtm, torch.Tensor): + if not(torch.isfinite(dtm).all()): + warnings.warn("Overflow/infinite value encountered while computing 'dtm'", RuntimeWarning) # We compute too many powers, 1/p in knn then q in dtm, 1/q in dtm then q or some log in the caller. # Add option to skip the final root? return dtm @@ -163,6 +169,11 @@ class DTMDensity: distances = self.knn.transform(X) distances = distances ** q dtm = (distances * weights).sum(-1) + with warnings.catch_warnings(): + import torch + if isinstance(dtm, torch.Tensor): + if not(torch.isfinite(dtm).all()): + warnings.warn("Overflow/infinite value encountered while computing 'dtm' for density", RuntimeWarning) if self.normalize: dtm /= (np.arange(1, k + 1) ** (q / dim) * weights).sum() density = dtm ** (-dim / q) diff --git a/src/python/test/test_dtm.py b/src/python/test/test_dtm.py index c29471cf..52468d0f 100755 --- a/src/python/test/test_dtm.py +++ b/src/python/test/test_dtm.py @@ -97,7 +97,15 @@ def test_dtm_overflow_warnings(): for impl in impl_warn: dtm = DistanceToMeasure(2, q=10000, implementation=impl) r = dtm.fit_transform(pts) - assert len(w) == 2 + assert len(w) == 3 for i in range(len(w)): assert issubclass(w[i].category, RuntimeWarning) assert "Overflow" in str(w[i].message) + +def test_density_overflow_warning(): + distances = numpy.array([[10., 100.], [10000000000000., 10.]]) + with warnings.catch_warnings(record=True) as w: + density = DTMDensity(k=2, q=100000, implementation="keops", dim=1).fit_transform(distances) + assert len(w) == 1 + assert issubclass(w[0].category, RuntimeWarning) + assert "Overflow" in str(w[0].message) -- cgit v1.2.3 From 36959807d5091b79aedabbc67c363dd761c9d5ee Mon Sep 17 00:00:00 2001 From: Hind-M Date: Thu, 21 Oct 2021 17:08:43 +0200 Subject: Factorize cpp and python torus tests implementations --- src/python/test/test_datasets_generators.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'src/python/test') diff --git a/src/python/test/test_datasets_generators.py b/src/python/test/test_datasets_generators.py index 4c087c57..e2d300e0 100755 --- a/src/python/test/test_datasets_generators.py +++ b/src/python/test/test_datasets_generators.py @@ -18,22 +18,16 @@ def test_sphere(): with pytest.raises(ValueError): points.sphere(n_samples = 10, ambient_dim = 2, radius = 1., sample = 'other') -def test_torus(): - assert points.ctorus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) - assert points.ctorus(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) - - assert points.ctorus(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) - assert points.ctorus(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) - - with pytest.raises(ValueError): - points.ctorus(n_samples = 10, dim = 4, sample = 'other') +def _basic_torus(impl): + assert impl(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) + assert impl(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) -def test_torus_full_python(): - assert points.torus(n_samples = 64, dim = 3, sample = 'random').shape == (64, 6) - assert points.torus(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) - - assert points.torus(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) - assert points.torus(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) + assert impl(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) + assert impl(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) with pytest.raises(ValueError): - points.torus(n_samples = 10, dim = 4, sample = 'other') + impl(n_samples = 10, dim = 4, sample = 'other') + +def test_torus(): + for torus_impl in [points.torus, points.ctorus]: + _basic_torus(torus_impl) -- cgit v1.2.3 From 0bf357f6346fab6edf96d580a9195c2acbb79bae Mon Sep 17 00:00:00 2001 From: Hind-M Date: Mon, 25 Oct 2021 10:23:45 +0200 Subject: Revert "Add warnings in dtm.py for DistanceToMeasure and DTMDensity" This reverts commit f461f050ee8bad509814b4851ab7ae8f43502962. --- src/python/gudhi/point_cloud/dtm.py | 11 ----------- src/python/test/test_dtm.py | 10 +--------- 2 files changed, 1 insertion(+), 20 deletions(-) (limited to 'src/python/test') diff --git a/src/python/gudhi/point_cloud/dtm.py b/src/python/gudhi/point_cloud/dtm.py index 96a9e7bf..55ac58e6 100644 --- a/src/python/gudhi/point_cloud/dtm.py +++ b/src/python/gudhi/point_cloud/dtm.py @@ -9,7 +9,6 @@ from .knn import KNearestNeighbors import numpy as np -import warnings __author__ = "Marc Glisse" __copyright__ = "Copyright (C) 2020 Inria" @@ -67,11 +66,6 @@ class DistanceToMeasure: distances = distances ** self.q dtm = distances.sum(-1) / self.k dtm = dtm ** (1.0 / self.q) - with warnings.catch_warnings(): - import torch - if isinstance(dtm, torch.Tensor): - if not(torch.isfinite(dtm).all()): - warnings.warn("Overflow/infinite value encountered while computing 'dtm'", RuntimeWarning) # We compute too many powers, 1/p in knn then q in dtm, 1/q in dtm then q or some log in the caller. # Add option to skip the final root? return dtm @@ -169,11 +163,6 @@ class DTMDensity: distances = self.knn.transform(X) distances = distances ** q dtm = (distances * weights).sum(-1) - with warnings.catch_warnings(): - import torch - if isinstance(dtm, torch.Tensor): - if not(torch.isfinite(dtm).all()): - warnings.warn("Overflow/infinite value encountered while computing 'dtm' for density", RuntimeWarning) if self.normalize: dtm /= (np.arange(1, k + 1) ** (q / dim) * weights).sum() density = dtm ** (-dim / q) diff --git a/src/python/test/test_dtm.py b/src/python/test/test_dtm.py index 52468d0f..c29471cf 100755 --- a/src/python/test/test_dtm.py +++ b/src/python/test/test_dtm.py @@ -97,15 +97,7 @@ def test_dtm_overflow_warnings(): for impl in impl_warn: dtm = DistanceToMeasure(2, q=10000, implementation=impl) r = dtm.fit_transform(pts) - assert len(w) == 3 + assert len(w) == 2 for i in range(len(w)): assert issubclass(w[i].category, RuntimeWarning) assert "Overflow" in str(w[i].message) - -def test_density_overflow_warning(): - distances = numpy.array([[10., 100.], [10000000000000., 10.]]) - with warnings.catch_warnings(record=True) as w: - density = DTMDensity(k=2, q=100000, implementation="keops", dim=1).fit_transform(distances) - assert len(w) == 1 - assert issubclass(w[0].category, RuntimeWarning) - assert "Overflow" in str(w[0].message) -- cgit v1.2.3 From bb8c4994b89fb6bfdd80b76912acadf6197f93cc Mon Sep 17 00:00:00 2001 From: Hind-M Date: Tue, 26 Oct 2021 13:59:44 +0200 Subject: Add comments and some minor changes following code review --- src/python/doc/datasets_generators.rst | 13 +++++++------ src/python/gudhi/datasets/generators/_points.cc | 2 +- src/python/gudhi/datasets/generators/points.py | 6 +++--- src/python/test/test_datasets_generators.py | 2 ++ 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src/python/test') diff --git a/src/python/doc/datasets_generators.rst b/src/python/doc/datasets_generators.rst index c0bbb973..3700b8a2 100644 --- a/src/python/doc/datasets_generators.rst +++ b/src/python/doc/datasets_generators.rst @@ -48,22 +48,23 @@ You can also generate points on a torus. Two functions are available and give the same output: the first one depends on **CGAL** and the second does not and consists of full python code. -On another hand, two sample types are provided : you can either generate i.i.d. points on a d-torus in :math:`R^{2d}` *randomly* or on a *grid*. +On another hand, two sample types are provided: you can either generate i.i.d. points on a d-torus in :math:`R^{2d}` *randomly* or on a *grid*. -First function : **ctorus** +First function: **ctorus** """"""""""""""""""""""""""" The user should provide the number of points to be generated on the torus :code:`n_samples`, and the dimension :code:`dim` of the torus on which points would be generated in :math:`R^{2dim}`. The :code:`sample` argument is optional and is set to **'random'** by default. In this case, the returned generated points would be an array of shape :math:`(n\_samples, 2*dim)`. -Otherwise, if set to **'grid'**, the points are generated on a grid and would be given as an array of shape : +Otherwise, if set to **'grid'**, the points are generated on a grid and would be given as an array of shape: .. math:: - ( [n\_samples^{1 \over {dim}}]^{dim}, 2*dim ) + ( ⌊n\_samples^{1 \over {dim}}⌋^{dim}, 2*dim ) +**Note 1:** The output array first shape is rounded down to the closest perfect :math:`dim^{th}` power. -**Note:** This version is recommended when the user wishes to use **'grid'** as sample type, or **'random'** with a relatively small number of samples (~ less than 150). +**Note 2:** This version is recommended when the user wishes to use **'grid'** as sample type, or **'random'** with a relatively small number of samples (~ less than 150). Example """"""" @@ -79,7 +80,7 @@ Example .. autofunction:: gudhi.datasets.generators.points.ctorus -Second function : **torus** +Second function: **torus** """"""""""""""""""""""""""" The user should provide the number of points to be generated on the torus :code:`n_samples` and the dimension :code:`dim` of the torus on which points would be generated in :math:`R^{2dim}`. diff --git a/src/python/gudhi/datasets/generators/_points.cc b/src/python/gudhi/datasets/generators/_points.cc index 536fa949..5d675930 100644 --- a/src/python/gudhi/datasets/generators/_points.cc +++ b/src/python/gudhi/datasets/generators/_points.cc @@ -110,7 +110,7 @@ PYBIND11_MODULE(_points, m) { :rtype: numpy array of float. The shape of returned numpy array is : if sample is 'random' : (n_samples, 2*dim). - if sample is 'grid' : ([n_samples**(1./dim)]**dim, 2*dim). + if sample is 'grid' : (⌊n_samples**(1./dim)⌋**dim, 2*dim), where shape[0] is rounded down to the closest perfect 'dim'th power. :returns: the generated points on a torus. )pbdoc"); } diff --git a/src/python/gudhi/datasets/generators/points.py b/src/python/gudhi/datasets/generators/points.py index 1995f769..7f4667af 100644 --- a/src/python/gudhi/datasets/generators/points.py +++ b/src/python/gudhi/datasets/generators/points.py @@ -36,15 +36,15 @@ def _generate_grid_points_on_torus(n_samples, dim): def torus(n_samples, dim, sample='random'): """ - Generate points on a dim-torus in R^2dim either randomly or on a grid + Generate points on a flat dim-torus in R^2dim either randomly or on a grid :param n_samples: The number of points to be generated. :param dim: The dimension of the torus on which points would be generated in R^2*dim. :param sample: The sample type of the generated points. Can be 'random' or 'grid'. :returns: numpy array containing the generated points on a torus. - The shape of returned numpy array is : + The shape of returned numpy array is: if sample is 'random' : (n_samples, 2*dim). - if sample is 'grid' : ([n_samples**(1./dim)]**dim, 2*dim). + if sample is 'grid' : (⌊n_samples**(1./dim)⌋**dim, 2*dim), where shape[0] is rounded down to the closest perfect 'dim'th power. """ if sample == 'random': # Generate points randomly diff --git a/src/python/test/test_datasets_generators.py b/src/python/test/test_datasets_generators.py index e2d300e0..933a763e 100755 --- a/src/python/test/test_datasets_generators.py +++ b/src/python/test/test_datasets_generators.py @@ -23,6 +23,8 @@ def _basic_torus(impl): assert impl(n_samples = 64, dim = 3, sample = 'grid').shape == (64, 6) assert impl(n_samples = 10, dim = 4, sample = 'random').shape == (10, 8) + + # Here 1**dim < n_samples < 2**dim, the output shape is therefore (1, 2*dim) = (1, 8), where shape[0] is rounded down to the closest perfect 'dim'th power assert impl(n_samples = 10, dim = 4, sample = 'grid').shape == (1, 8) with pytest.raises(ValueError): -- cgit v1.2.3 From 2ffbd4d63afd59b9d1995a7755db087b31f9c998 Mon Sep 17 00:00:00 2001 From: Hind-M Date: Wed, 27 Oct 2021 10:01:23 +0200 Subject: Add test to check outputs of the two torus implementations --- src/python/test/test_datasets_generators.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/python/test') diff --git a/src/python/test/test_datasets_generators.py b/src/python/test/test_datasets_generators.py index 933a763e..91ec4a65 100755 --- a/src/python/test/test_datasets_generators.py +++ b/src/python/test/test_datasets_generators.py @@ -33,3 +33,7 @@ def _basic_torus(impl): def test_torus(): for torus_impl in [points.torus, points.ctorus]: _basic_torus(torus_impl) + # Check that the two versions (torus and ctorus) generate the same output + assert points.ctorus(n_samples = 64, dim = 3, sample = 'random').all() == points.torus(n_samples = 64, dim = 3, sample = 'random').all() + assert points.ctorus(n_samples = 64, dim = 3, sample = 'grid').all() == points.torus(n_samples = 64, dim = 3, sample = 'grid').all() + assert points.ctorus(n_samples = 10, dim = 3, sample = 'grid').all() == points.torus(n_samples = 10, dim = 3, sample = 'grid').all() -- cgit v1.2.3