diff options
-rw-r--r-- | src/Tangential_complex/benchmark/benchmark_tc.cpp | 2 | ||||
-rw-r--r-- | src/common/include/gudhi/random_point_generators.h | 12 | ||||
-rw-r--r-- | src/common/utilities/off_file_from_shape_generator.cpp | 2 | ||||
-rw-r--r-- | src/python/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/python/doc/datasets_generators.inc | 14 | ||||
-rw-r--r-- | src/python/doc/datasets_generators.rst | 105 | ||||
-rw-r--r-- | src/python/doc/examples.rst | 1 | ||||
-rw-r--r-- | src/python/doc/img/sphere_3d.png | bin | 0 -> 529148 bytes | |||
-rw-r--r-- | src/python/doc/index.rst | 5 | ||||
-rw-r--r-- | src/python/example/alpha_complex_from_generated_points_on_sphere_example.py | 4 | ||||
-rw-r--r-- | src/python/gudhi/datasets/generators/_points.cc | 120 | ||||
-rw-r--r-- | src/python/gudhi/datasets/generators/points.cc | 68 | ||||
-rw-r--r-- | src/python/gudhi/datasets/generators/points.py | 56 | ||||
-rwxr-xr-x | src/python/test/test_datasets_generators.py | 39 |
14 files changed, 355 insertions, 79 deletions
diff --git a/src/Tangential_complex/benchmark/benchmark_tc.cpp b/src/Tangential_complex/benchmark/benchmark_tc.cpp index e3b2a04f..6da1425f 100644 --- a/src/Tangential_complex/benchmark/benchmark_tc.cpp +++ b/src/Tangential_complex/benchmark/benchmark_tc.cpp @@ -704,7 +704,7 @@ int main() { points = Gudhi::generate_points_on_torus_d<Kernel>( num_points, intrinsic_dim, - param1 == "Y", // uniform + (param1 == "Y") ? "grid" : "random", // grid or random sample type std::atof(param2.c_str())); // radius_noise_percentage } else if (input == "generate_klein_bottle_3D") { points = Gudhi::generate_points_on_klein_bottle_3D<Kernel>( diff --git a/src/common/include/gudhi/random_point_generators.h b/src/common/include/gudhi/random_point_generators.h index 33fb182d..25a7392d 100644 --- a/src/common/include/gudhi/random_point_generators.h +++ b/src/common/include/gudhi/random_point_generators.h @@ -185,7 +185,7 @@ std::vector<typename Kernel::Point_d> generate_points_on_torus_3D(std::size_t nu // "Private" function used by generate_points_on_torus_d template <typename Kernel, typename OutputIterator> -static void generate_uniform_points_on_torus_d(const Kernel &k, int dim, std::size_t num_slices, +static void generate_grid_points_on_torus_d(const Kernel &k, int dim, std::size_t num_slices, OutputIterator out, double radius_noise_percentage = 0., std::vector<typename Kernel::FT> current_point = @@ -208,14 +208,14 @@ static void generate_uniform_points_on_torus_d(const Kernel &k, int dim, std::si double alpha = two_pi * slice_idx / num_slices; cp2.push_back(radius_noise_ratio * std::cos(alpha)); cp2.push_back(radius_noise_ratio * std::sin(alpha)); - generate_uniform_points_on_torus_d( + generate_grid_points_on_torus_d( k, dim, num_slices, out, radius_noise_percentage, cp2); } } } template <typename Kernel> -std::vector<typename Kernel::Point_d> generate_points_on_torus_d(std::size_t num_points, int dim, bool uniform = false, +std::vector<typename Kernel::Point_d> generate_points_on_torus_d(std::size_t num_points, int dim, std::string sample = "random", double radius_noise_percentage = 0.) { using namespace boost::math::double_constants; @@ -226,9 +226,9 @@ std::vector<typename Kernel::Point_d> generate_points_on_torus_d(std::size_t num std::vector<Point> points; points.reserve(num_points); - if (uniform) { - std::size_t num_slices = (std::size_t)std::pow(num_points, 1. / dim); - generate_uniform_points_on_torus_d( + if (sample == "grid") { + std::size_t num_slices = (std::size_t)std::pow(num_points + .5, 1. / dim); // add .5 to avoid rounding down with numerical approximations + generate_grid_points_on_torus_d( k, dim, num_slices, std::back_inserter(points), radius_noise_percentage); } else { for (std::size_t i = 0; i < num_points;) { diff --git a/src/common/utilities/off_file_from_shape_generator.cpp b/src/common/utilities/off_file_from_shape_generator.cpp index 6efef4fc..71ede434 100644 --- a/src/common/utilities/off_file_from_shape_generator.cpp +++ b/src/common/utilities/off_file_from_shape_generator.cpp @@ -135,7 +135,7 @@ int main(int argc, char **argv) { if (dimension == 3) points = Gudhi::generate_points_on_torus_3D<K>(points_number, dimension, radius, radius/2.); else - points = Gudhi::generate_points_on_torus_d<K>(points_number, dimension, true); + points = Gudhi::generate_points_on_torus_d<K>(points_number, dimension, "grid"); break; case Data_shape::klein: switch (dimension) { diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 96107cfe..4a017251 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -163,7 +163,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYBIND11_MODULES "${GUDHI_PYBIND11_MODULES}'hera/wasserstein', ") set(GUDHI_PYBIND11_MODULES "${GUDHI_PYBIND11_MODULES}'hera/bottleneck', ") if (NOT CGAL_VERSION VERSION_LESS 4.11.0) - set(GUDHI_PYBIND11_MODULES "${GUDHI_PYBIND11_MODULES}'datasets/generators/points', ") + set(GUDHI_PYBIND11_MODULES "${GUDHI_PYBIND11_MODULES}'datasets/generators/_points', ") set(GUDHI_PYBIND11_MODULES "${GUDHI_PYBIND11_MODULES}'bottleneck', ") set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}'nerve_gic', ") endif () @@ -277,6 +277,7 @@ if(PYTHONINTERP_FOUND) file(COPY "gudhi/hera/__init__.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/hera") file(COPY "gudhi/datasets" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi" FILES_MATCHING PATTERN "*.py") + # Some files for pip package file(COPY "introduction.rst" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/") file(COPY "pyproject.toml" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/") @@ -455,6 +456,9 @@ if(PYTHONINTERP_FOUND) # Euclidean witness add_gudhi_py_test(test_euclidean_witness_complex) + # Datasets generators + add_gudhi_py_test(test_datasets_generators) # TODO separate full python datasets generators in another test file independant from CGAL ? + endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) # Cubical diff --git a/src/python/doc/datasets_generators.inc b/src/python/doc/datasets_generators.inc new file mode 100644 index 00000000..8d169275 --- /dev/null +++ b/src/python/doc/datasets_generators.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 40 30 + + +-----------------------------------+--------------------------------------------+--------------------------------------------------------------------------------------+ + | .. figure:: | Datasets generators (points). | :Authors: Hind Montassif | + | img/sphere_3d.png | | | + | | | :Since: GUDHI 3.5.0 | + | | | | + | | | :License: MIT (`LGPL v3 </licensing/>`_) | + | | | | + | | | :Requires: `CGAL <installation.html#cgal>`_ | + +-----------------------------------+--------------------------------------------+--------------------------------------------------------------------------------------+ + | * :doc:`datasets_generators` | + +-----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/doc/datasets_generators.rst b/src/python/doc/datasets_generators.rst new file mode 100644 index 00000000..6f36bce1 --- /dev/null +++ b/src/python/doc/datasets_generators.rst @@ -0,0 +1,105 @@ + +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +=========================== +Datasets generators manual +=========================== + +We provide the generation of different customizable datasets to use as inputs for Gudhi complexes and data structures. + + +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 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. + +The generated points are given as an array of shape :math:`(n\_samples, ambient\_dim)`. + +Example +""""""" + +.. code-block:: python + + 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") + + # Create an alpha complex from the generated points + alpha_complex = AlphaComplex(points = gen_points) + +.. autofunction:: gudhi.datasets.generators.points.sphere + +Points on a flat torus +^^^^^^^^^^^^^^^^ + +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*. + +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: + +.. math:: + + ( ⌊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 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 +""""""" +.. code-block:: python + + from gudhi.datasets.generators import points + + # Generate 50 points randomly on a torus in R^6 + gen_points = points.ctorus(n_samples = 50, dim = 3) + + # Generate 27 points on a torus as a grid in R^6 + gen_points = points.ctorus(n_samples = 50, dim = 3, sample = 'grid') + +.. autofunction:: gudhi.datasets.generators.points.ctorus + +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. +The other allowed value of sample type is **'grid'**. + +**Note:** This version is recommended when the user wishes to use **'random'** as sample type with a great number of samples and a low dimension. + +Example +""""""" +.. code-block:: python + + 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) + + # Generate 27 points on a torus as a grid in R^6 + gen_points = points.torus(n_samples = 50, dim = 3, sample = 'grid') + + +.. autofunction:: gudhi.datasets.generators.points.torus diff --git a/src/python/doc/examples.rst b/src/python/doc/examples.rst index 76e5d4c7..1442f185 100644 --- a/src/python/doc/examples.rst +++ b/src/python/doc/examples.rst @@ -8,6 +8,7 @@ Examples .. only:: builder_html * :download:`alpha_complex_diagram_persistence_from_off_file_example.py <../example/alpha_complex_diagram_persistence_from_off_file_example.py>` + * :download:`alpha_complex_from_generated_points_on_sphere_example.py <../example/alpha_complex_from_generated_points_on_sphere_example.py>` * :download:`alpha_complex_from_points_example.py <../example/alpha_complex_from_points_example.py>` * :download:`alpha_rips_persistence_bottleneck_distance.py <../example/alpha_rips_persistence_bottleneck_distance.py>` * :download:`bottleneck_basic_example.py <../example/bottleneck_basic_example.py>` diff --git a/src/python/doc/img/sphere_3d.png b/src/python/doc/img/sphere_3d.png Binary files differnew file mode 100644 index 00000000..70f3184f --- /dev/null +++ b/src/python/doc/img/sphere_3d.png diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 040e57a4..2d7921ae 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -91,3 +91,8 @@ Clustering ********** .. include:: clustering.inc + +Datasets generators +******************* + +.. include:: datasets_generators.inc diff --git a/src/python/example/alpha_complex_from_generated_points_on_sphere_example.py b/src/python/example/alpha_complex_from_generated_points_on_sphere_example.py index 267e6436..3558077e 100644 --- a/src/python/example/alpha_complex_from_generated_points_on_sphere_example.py +++ b/src/python/example/alpha_complex_from_generated_points_on_sphere_example.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from gudhi.datasets.generators import points +from gudhi.datasets.generators import _points from gudhi import AlphaComplex @@ -22,7 +22,7 @@ print("#####################################################################") print("AlphaComplex creation from generated points on sphere") -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 alpha_complex = AlphaComplex(points = gen_points) diff --git a/src/python/gudhi/datasets/generators/_points.cc b/src/python/gudhi/datasets/generators/_points.cc new file mode 100644 index 00000000..70ce4925 --- /dev/null +++ b/src/python/gudhi/datasets/generators/_points.cc @@ -0,0 +1,120 @@ +/* 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 + */ + +#include <pybind11/pybind11.h> +#include <pybind11/numpy.h> + +#include <gudhi/random_point_generators.h> +#include <gudhi/Debug_utils.h> + +#include <CGAL/Epick_d.h> + +namespace py = pybind11; + + +typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Kern; + +py::array_t<double> generate_points_on_sphere(size_t n_samples, int ambient_dim, double radius, std::string sample) { + + if (sample != "random") { + throw pybind11::value_error("This sample type is not supported"); + } + + py::array_t<double> points({n_samples, (size_t)ambient_dim}); + + py::buffer_info buf = points.request(); + double *ptr = static_cast<double *>(buf.ptr); + + GUDHI_CHECK(n_samples == buf.shape[0], "Py array first dimension not matching n_samples on sphere"); + GUDHI_CHECK(ambient_dim == buf.shape[1], "Py array second dimension not matching the ambient space dimension"); + + + std::vector<typename Kern::Point_d> points_generated; + + { + py::gil_scoped_release release; + points_generated = Gudhi::generate_points_on_sphere_d<Kern>(n_samples, ambient_dim, radius); + } + + for (size_t i = 0; i < n_samples; i++) + for (int j = 0; j < ambient_dim; j++) + ptr[i*ambient_dim+j] = points_generated[i][j]; + + return points; +} + +py::array_t<double> generate_points_on_torus(size_t n_samples, int dim, std::string sample) { + + if ( (sample != "random") && (sample != "grid")) { + throw pybind11::value_error("This sample type is not supported"); + } + + std::vector<typename Kern::Point_d> points_generated; + + { + py::gil_scoped_release release; + points_generated = Gudhi::generate_points_on_torus_d<Kern>(n_samples, dim, sample); + } + + size_t npoints = points_generated.size(); + + GUDHI_CHECK(2*dim == points_generated[0].size(), "Py array second dimension not matching the double torus dimension"); + + py::array_t<double> points({npoints, (size_t)2*dim}); + + py::buffer_info buf = points.request(); + double *ptr = static_cast<double *>(buf.ptr); + + for (size_t i = 0; i < npoints; i++) + for (int j = 0; j < 2*dim; j++) + ptr[i*(2*dim)+j] = points_generated[i][j]; + + return points; +} + +PYBIND11_MODULE(_points, m) { + m.attr("__license__") = "LGPL v3"; + + m.def("sphere", &generate_points_on_sphere, + py::arg("n_samples"), py::arg("ambient_dim"), + py::arg("radius") = 1., py::arg("sample") = "random", + R"pbdoc( + Generate random i.i.d. points uniformly on a (d-1)-sphere in R^d + + :param n_samples: The number of points to be generated. + :type n_samples: integer + :param ambient_dim: The ambient dimension d. + :type ambient_dim: integer + :param radius: The radius. Default value is `1.`. + :type radius: float + :param sample: The sample type. Default and only available value is `"random"`. + :type sample: string + :rtype: numpy array of float + :returns: the generated points on a sphere. + )pbdoc"); + + 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 or as a grid + + :param n_samples: The number of points to be generated. + :type n_samples: integer + :param dim: The dimension of the torus on which points would be generated in R^2*dim. + :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. + 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), 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.cc b/src/python/gudhi/datasets/generators/points.cc deleted file mode 100644 index d658946b..00000000 --- a/src/python/gudhi/datasets/generators/points.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* 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 - */ - -#include <pybind11/pybind11.h> -#include <pybind11/numpy.h> - -#include <gudhi/random_point_generators.h> -#include <gudhi/Debug_utils.h> - -#include <CGAL/Epick_d.h> - -namespace py = pybind11; - - -typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Kern; - -py::array_t<double> generate_points_on_sphere(size_t n_samples, int ambient_dim, double radius, std::string sample) { - - if (sample != "random") { - throw pybind11::value_error("This sample type is not supported"); - } - - py::array_t<double> points({n_samples, (size_t)ambient_dim}); - - py::buffer_info buf = points.request(); - double *ptr = static_cast<double *>(buf.ptr); - - GUDHI_CHECK(n_samples == buf.shape[0], "Py array first dimension not matching n_samples on sphere"); - GUDHI_CHECK(ambient_dim == buf.shape[1], "Py array second dimension not matching the ambient space dimension"); - - - py::gil_scoped_release release; - auto points_generated = Gudhi::generate_points_on_sphere_d<Kern>(n_samples, ambient_dim, radius); - - for (size_t i = 0; i < n_samples; i++) - for (int j = 0; j < ambient_dim; j++) - ptr[i*ambient_dim+j] = points_generated[i][j]; - - return points; -} - -PYBIND11_MODULE(points, m) { - m.attr("__license__") = "LGPL v3"; - m.def("sphere", &generate_points_on_sphere, - py::arg("n_samples"), py::arg("ambient_dim"), - py::arg("radius") = 1., py::arg("sample") = "random", - R"pbdoc( - Generate random i.i.d. points uniformly on a (d-1)-sphere in R^d - - :param n_samples: The number of points to be generated. - :type n_samples: integer - :param ambient_dim: The ambient dimension d. - :type ambient_dim: integer - :param radius: The radius. Default value is `1.`. - :type radius: float - :param sample: The sample type. Default and only available value is `"random"`. - :type sample: string - :rtype: numpy array of float - :returns: the generated points on a sphere. - )pbdoc"); -} diff --git a/src/python/gudhi/datasets/generators/points.py b/src/python/gudhi/datasets/generators/points.py new file mode 100644 index 00000000..cf97777d --- /dev/null +++ b/src/python/gudhi/datasets/generators/points.py @@ -0,0 +1,56 @@ +# 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 + +import numpy as np + +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 + alpha = 2*np.pi*np.random.rand(n_samples*dim) + + # Based on angles, construct points of size n_samples*dim on a circle and reshape the result in a n_samples*2*dim array + array_points = np.column_stack([np.cos(alpha), np.sin(alpha)]).reshape(-1, 2*dim) + + return array_points + +def _generate_grid_points_on_torus(n_samples, dim): + + # Generate points on a dim-torus as a grid + n_samples_grid = int((n_samples+.5)**(1./dim)) # add .5 to avoid rounding down with numerical approximations + alpha = np.linspace(0, 2*np.pi, n_samples_grid, endpoint=False) + + array_points = np.column_stack([np.cos(alpha), np.sin(alpha)]) + array_points_idx = np.empty([n_samples_grid]*dim + [dim], dtype=int) + for i, x in enumerate(np.ix_(*([np.arange(n_samples_grid)]*dim))): + array_points_idx[...,i] = x + return array_points[array_points_idx].reshape(-1, 2*dim) + +def torus(n_samples, dim, sample='random'): + """ + 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: + if sample is 'random' : (n_samples, 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 + return _generate_random_points_on_torus(n_samples, dim) + elif sample == 'grid': + # Generate points on a grid + return _generate_grid_points_on_torus(n_samples, dim) + else: + raise ValueError("Sample type '{}' is not supported".format(sample)) diff --git a/src/python/test/test_datasets_generators.py b/src/python/test/test_datasets_generators.py new file mode 100755 index 00000000..91ec4a65 --- /dev/null +++ b/src/python/test/test_datasets_generators.py @@ -0,0 +1,39 @@ +""" 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 + +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 _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) + + 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): + impl(n_samples = 10, dim = 4, sample = 'other') + +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() |