diff --git a/README.rst b/README.rst index 7511028..de6bdd1 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -.. image:: https://www.giotto.ai/static/vector/logo.svg - :width: 850 +.. image:: https://raw.githubusercontent.com/giotto-ai/pyflagser/master/doc/images/Giotto_logo_RGB.svg + :width: 590 |Azure|_ |Azure-cov|_ |Azure-test|_ @@ -22,18 +22,12 @@ pyflagser ========= -pyflagser s a python API for the flagser C++ library by Daniel Lütgehetmann which computes the homology of directed flag complexes. Please check out the original `luetge/flagser `_ GitHub repository for more information. - -Website: https://giotto.ai - +``pyflagser`` is a python API for the flagser C++ library by Daniel Lütgehetmann which computes the homology of directed flag complexes. Please check out the original `luetge/flagser `_ GitHub repository for more information. Project genesis --------------- -pyflagser is the result of a collaborative effort between `L2F SA -`_, the `Laboratory for Topology and Neuroscience -`_ at EPFL, and the `Institute of Reconfigurable & Embedded Digital Systems (REDS) -`_ of HEIG-VD. +``pyflagser`` is the result of a collaborative effort between `L2F SA `_, the `Laboratory for Topology and Neuroscience `_ at EPFL, and the `Institute of Reconfigurable & Embedded Digital Systems (REDS) `_ of HEIG-VD. Installation ------------ @@ -41,34 +35,28 @@ Installation Dependencies ~~~~~~~~~~~~ -pyflagser requires: +``pyflagser`` requires: - Python (>= 3.6) -- numpy (>= 1.17.0) -- scipy (>= 0.17.0) - -For running the examples jupyter, matplotlib and plotly are required. +- NumPy (>= 1.17.0) +- SciPy (>= 0.17.0) User installation ~~~~~~~~~~~~~~~~~ -If you already have a working installation of numpy and scipy, -the easiest way to install pyflagser is using ``pip`` :: +If you already have a working installation of numpy and scipy, the easiest way to install pyflagser is using ``pip`` :: - pip install -U pyflagser + python -m pip install -U pyflagser Documentation ------------- -- HTML documentation (stable release): https://docs-pyflagser.giotto.ai +API reference (stable release): https://docs-pyflagser.giotto.ai Contributing ------------ -We welcome new contributors of all experience levels. The Giotto -community goals are to be helpful, welcoming, and effective. To learn more about -making a contribution to pyflagser, please see the `CONTRIBUTING.rst -`_ file. +We welcome new contributors of all experience levels. The Giotto community goals are to be helpful, welcoming, and effective. To learn more about making a contribution to ``pyflagser``, please see the `CONTRIBUTING.rst `_ file. Developer installation ~~~~~~~~~~~~~~~~~~~~~~ @@ -95,15 +83,14 @@ From the cloned repository's root directory, run .. code-block:: bash - pip install -e . + python -m pip install -e ".[tests]" This way, you can pull the library's latest changes and make them immediately available on your machine. Testing ~~~~~~~ -After installation, you can launch the test suite from outside the -source directory:: +After installation, you can launch the test suite from outside the source directory:: pytest pyflagser diff --git a/RELEASE.rst b/RELEASE.rst index 5513248..9e1eac0 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,5 +1,26 @@ +Release 0.4.1 +============= + +Bug Fixes +--------- +A bug was fixed which caused some computations to hang when the prime for the finite field of coefficients used is greater than 2. + +Backwards-Incompatible Changes +------------------------------ +None. + +Thanks to our Contributors +-------------------------- + +This release contains contributions from many people: + +Julian Burella Pérez and Umberto Lupo. + +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. + + Release 0.4.0 -============== +============= Major Features and Improvements ------------------------------- @@ -24,12 +45,11 @@ This release contains contributions from many people: Guillaume Tauzin, Umberto Lupo, and Julian Burella Pérez. -We are also grateful to all who filed issues or helped resolve them, asked and -answered questions, and were part of inspiring discussions. +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. Release 0.3.1 -============== +============= Major Features and Improvements ------------------------------- @@ -52,8 +72,7 @@ This release contains contributions from many people: Umberto Lupo and Guillaume Tauzin. -We are also grateful to all who filed issues or helped resolve them, asked and -answered questions, and were part of inspiring discussions. +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. Release 0.3.0 @@ -124,12 +143,11 @@ This release contains contributions from many people: Guillaume Tauzin, Umberto Lupo, and Julian Burella Pérez. -We are also grateful to all who filed issues or helped resolve them, asked and -answered questions, and were part of inspiring discussions. +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. Release 0.2.1 -============== +============= Major Features and Improvements ------------------------------- @@ -158,12 +176,11 @@ This release contains contributions from many people: Julian Burella Pérez, Umberto Lupo, and Guillaume Tauzin. -We are also grateful to all who filed issues or helped resolve them, asked and -answered questions, and were part of inspiring discussions. +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. Release 0.2.0 -============== +============= Major Features and Improvements ------------------------------- @@ -187,12 +204,11 @@ This release contains contributions from many people: Julian Burella Pérez, Umberto Lupo, and Guillaume Tauzin. -We are also grateful to all who filed issues or helped resolve them, asked and -answered questions, and were part of inspiring discussions. +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. Release 0.1.0 -============== +============= Initial release of ``pyflagser``. @@ -220,5 +236,4 @@ This release contains contributions from many people: Guillaume Tauzin, Julian Burella Pérez and Umberto Lupo. -We are also grateful to all who filed issues or helped resolve them, asked and -answered questions, and were part of inspiring discussions. +We are also grateful to all who filed issues or helped resolve them, asked and answered questions, and were part of inspiring discussions. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b3f5a72..219fe4e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,7 +24,7 @@ jobs: failOnStderr: true displayName: 'Upgrade pip and setuptools' - - script: pip install -e ".[tests, doc]" + - script: python -m pip install -e ".[tests, doc]" failOnStderr: true displayName: 'Install dev environment' @@ -58,7 +58,7 @@ jobs: failOnStderr: true displayName: 'Upgrade pip and setuptools' - - script: pip install -e ".[tests, doc]" + - script: python -m pip install -e ".[tests, doc]" failOnStderr: true displayName: 'Install dev environment' @@ -91,12 +91,12 @@ jobs: versionSpec: '$(python.version)' - script: | - python -m pip install --upgrade pip setuptools + python -m python -m pip install --upgrade pip setuptools failOnStderr: true displayName: 'Upgrade pip and setuptools' - script: | - pip install -e ".[tests, doc]" + python -m pip install -e ".[tests, doc]" failOnStderr: true displayName: 'Install dev environment' @@ -148,12 +148,12 @@ jobs: python -m pip install --upgrade pip displayName: 'Upgrade pip and setuptools' - - script: pip install dist/*.whl + - script: python -m pip install dist/*.whl failOnStderr: true displayName: 'Install the wheels' - script: | - pip install pytest pytest-cov pytest-azurepipelines pytest-benchmark flake8 hypothesis + python -m pip install pytest pytest-cov pytest-timeout pytest-azurepipelines pytest-benchmark flake8 hypothesis mkdir tmp_test_cov cd tmp_test_cov python -m pyflagser.tests --webdl --no-cov --no-coverage-upload @@ -196,7 +196,7 @@ jobs: failOnStderr: true displayName: 'Upgrade pip and setuptools' - - script: pip install -e ".[tests, doc]" + - script: python -m pip install -e ".[tests, doc]" failOnStderr: true displayName: 'Install dev environment' @@ -212,18 +212,18 @@ jobs: displayName: 'Uninstall pyflagser dev' - script: | - pip install wheel + python -m pip install wheel python setup.py bdist_wheel failOnStderr: false displayName: 'Build the wheels' - script: | set -e - pip install twine + python -m pip install twine twine check dist/* displayName: 'Check distribution with twine' - - script: pip install dist/*.whl + - script: python -m pip install dist/*.whl failOnStderr: true displayName: 'Install the wheels' @@ -290,7 +290,7 @@ jobs: displayName: 'Upgrade pip and setuptools' - script: | - pip install -e ".[tests, doc]" + python -m pip install -e ".[tests, doc]" failOnStderr: true displayName: 'Install dev environment' @@ -307,12 +307,12 @@ jobs: - bash: | sed -i $'s/\r$//' README.rst - pip install wheel + python -m pip install wheel python setup.py bdist_wheel failOnStderr: false displayName: 'Build the wheels' - - bash: pip install dist/*.whl + - bash: python -m pip install dist/*.whl failOnStderr: true displayName: 'Install the wheels' diff --git a/doc/images/Giotto_logo_RGB.svg b/doc/images/Giotto_logo_RGB.svg new file mode 100644 index 0000000..b1cabfa --- /dev/null +++ b/doc/images/Giotto_logo_RGB.svg @@ -0,0 +1,76 @@ + +image/svg+xml + + + + + + + + + + + + + + + + diff --git a/flagser b/flagser index 3a5af59..702938a 160000 --- a/flagser +++ b/flagser @@ -1 +1 @@ -Subproject commit 3a5af591c165347cd65da7356084c4538cd7e806 +Subproject commit 702938a96353194278632f90ffa26df23046c44b diff --git a/pyflagser/_version.py b/pyflagser/_version.py index 1cfb3c2..8bb9f13 100644 --- a/pyflagser/_version.py +++ b/pyflagser/_version.py @@ -17,4 +17,4 @@ # 'X.Y.dev0' is the canonical version of 'X.Y.dev' # -__version__ = '0.4.0' +__version__ = '0.4.1' diff --git a/pyflagser/tests/test_flagser.py b/pyflagser/tests/test_flagser.py index 5097caf..fb39654 100644 --- a/pyflagser/tests/test_flagser.py +++ b/pyflagser/tests/test_flagser.py @@ -2,7 +2,7 @@ import os import numpy as np - +import pytest from numpy.testing import assert_almost_equal from pyflagser import load_unweighted_flag, load_weighted_flag, \ @@ -220,8 +220,16 @@ def test_filtrations_d5(flag_file, filtration): directed=False, filtration=filtration) for filt, tests in filtrations_results.items(): if filtration == filt: - tmp = np.array(res["dgms"]).tolist() - tmp2 = np.array(tests["dgms"]).tolist() + tmp = res["dgms"] + tmp2 = tests["dgms"] assert are_matrices_equal(tmp, tmp2), \ "Diagrams {} \n and {} \n are not equal"\ .format(tmp, tmp2) + + +@pytest.mark.timeout(30) +def test_higher_coefficients(): + """Regression test for issue #45""" + x = np.random.random((5, 5)) + np.fill_diagonal(x, 0.) + flagser_weighted(x, coeff=3) diff --git a/setup.py b/setup.py index 7df889c..7909ee9 100755 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ MAINTAINER_EMAIL = 'maintainers@giotto.ai' URL = 'https://github.com/giotto-ai/pyflagser' LICENSE = 'GNU AGPLv3' -DOWNLOAD_URL = 'https://github.com/giotto-ai/pyflagser/tarball/v0.4.0' +DOWNLOAD_URL = 'https://github.com/giotto-ai/pyflagser/tarball/v0.4.1' VERSION = __version__ # noqa CLASSIFIERS = ['Intended Audience :: Science/Research', 'Intended Audience :: Developers', @@ -50,6 +50,7 @@ EXTRAS_REQUIRE = { 'tests': [ 'pytest', + 'pytest-timeout', 'pytest-cov', 'pytest-azurepipelines', 'pytest-benchmark', diff --git a/src/flagser_bindings.cpp b/src/flagser_bindings.cpp index 91f2cfe..0ff350a 100644 --- a/src/flagser_bindings.cpp +++ b/src/flagser_bindings.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -9,7 +8,6 @@ namespace py = pybind11; - #ifdef USE_COEFFICIENTS PYBIND11_MODULE(flagser_coeff_pybind, m) { #else @@ -21,7 +19,7 @@ PYBIND11_MODULE(flagser_pybind, m) { m.attr("AVAILABLE_FILTRATIONS") = custom_filtration_computer; using PersistenceComputer = - persistence_computer_t; + persistence_computer_t; py::class_(m, "PersistenceComputer", py::module_local()) .def("get_euler_characteristic", @@ -29,15 +27,13 @@ PYBIND11_MODULE(flagser_pybind, m) { .def("get_betti_numbers", py::overload_cast<>(&PersistenceComputer::get_betti_numbers)) .def("get_betti_numbers", - py::overload_cast( - &PersistenceComputer::get_betti_numbers)) + py::overload_cast(&PersistenceComputer::get_betti_numbers)) .def("get_cell_count", py::overload_cast<>(&PersistenceComputer::get_cell_count)) - .def("get_cell_count", py::overload_cast( - &PersistenceComputer::get_cell_count)) + .def("get_cell_count", + py::overload_cast(&PersistenceComputer::get_cell_count)) .def("get_persistence_diagram", - py::overload_cast<>( - &PersistenceComputer::get_persistence_diagram)) + py::overload_cast<>(&PersistenceComputer::get_persistence_diagram)) .def("get_persistence_diagram", py::overload_cast( &PersistenceComputer::get_persistence_diagram)); @@ -48,42 +44,51 @@ PYBIND11_MODULE(flagser_pybind, m) { bool directed, coefficient_t modulus, signed int approximation, std::string filtration) { + flagser_parameters params; + // Save std::cout status auto cout_buff = std::cout.rdbuf(); - // flagser's routine needs to be passed command line arguments - named_arguments_t named_arguments; - - // Passing minimum dimension as a command line argument - std::string str_min = std::to_string(min_dim); - named_arguments["min-dim"] = str_min.c_str(); + // Minimum dimension parameter + params.min_dimension = min_dim; - // Passing maximum dimension as a command line argument + // Maximum dimension parameter unsigned short effective_max_dim = max_dim; if (max_dim < 0) { effective_max_dim = std::numeric_limits::max(); } - std::string str_max = std::to_string(effective_max_dim); - named_arguments["max-dim"] = str_max.c_str(); + params.max_dimension = effective_max_dim; + + // Filtration argument + params.filtration_algorithm.reset(get_filtration_computer(filtration)); - // Passing filtration as a command line argument - named_arguments["filtration"] = filtration.c_str(); + // Modulus/Coefficient parameter + params.modulus = modulus; + + // If approximation is negative it falls back to type::numeric_limits + params.approximate_computation = approximation ? true : false; + params.max_entries = + params.approximate_computation > 0 ? approximation : params.max_entries; + + // Directed parameter + params.directed = directed; // Output file is not used but set to an arbitrary file - named_arguments["out"] = "output_flagser_file"; + params.output_name = std::string("output_flagser_file"); // Remove output file if already present - remove(named_arguments["out"]); + remove(params.output_name.c_str()); // Building the filtered directed graph - auto graph = filtered_directed_graph_t(vertices, directed); + auto graph = filtered_directed_graph_t(vertices, params.directed); HAS_EDGE_FILTRATION has_edge_filtration = - HAS_EDGE_FILTRATION::TOO_EARLY_TO_DECIDE; + HAS_EDGE_FILTRATION::TOO_EARLY_TO_DECIDE; // If we have at least one edge if (edges.size() && has_edge_filtration == HAS_EDGE_FILTRATION::MAYBE) { - // If the edge has three components, the last is the filtration value + // If the edge has three components, the last is the filtration + // value has_edge_filtration = edges[0].size() == 2 ? HAS_EDGE_FILTRATION::NO : HAS_EDGE_FILTRATION::YES; } @@ -107,22 +112,17 @@ PYBIND11_MODULE(flagser_pybind, m) { } } - // If approximation is negative it falls back to type::numeric_limits - size_t max_entries = - approximation >= 0 ? approximation : std::numeric_limits::max(); - // Disable cout std::cout.rdbuf(nullptr); // Running flagser's compute_homology routine - auto subgraph_persistence_computer = - compute_homology(graph, named_arguments, max_entries, modulus); + auto subgraph_persistence_computer = compute_homology(graph, params); // Re-enable again cout std::cout.rdbuf(cout_buff); // Remove generate output file - if (remove(named_arguments["out"]) != 0) + if (remove(params.output_name.c_str()) != 0) perror("Error deleting flagser output file"); return subgraph_persistence_computer; diff --git a/src/flagser_count_bindings.cpp b/src/flagser_count_bindings.cpp index 74e99ff..4a945cf 100644 --- a/src/flagser_count_bindings.cpp +++ b/src/flagser_count_bindings.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -10,7 +9,6 @@ namespace py = pybind11; PYBIND11_MODULE(flagser_count_pybind, m) { - m.doc() = "Python interface for flagser_count"; m.def("compute_cell_count", [](std::vector& vertices, @@ -19,14 +17,14 @@ PYBIND11_MODULE(flagser_count_pybind, m) { // Save std::cout status auto cout_buff = std::cout.rdbuf(); - // flagser-count's routine needs to be passed command line arguments - named_arguments_t named_arguments; + // flagser-count's parameters + flagser_parameters params; // Building the filtered directed graph auto graph = filtered_directed_graph_t(vertices, directed); HAS_EDGE_FILTRATION has_edge_filtration = - HAS_EDGE_FILTRATION::TOO_EARLY_TO_DECIDE; + HAS_EDGE_FILTRATION::TOO_EARLY_TO_DECIDE; // If we have at least one edge if (edges.size() && has_edge_filtration == HAS_EDGE_FILTRATION::MAYBE) { @@ -58,7 +56,7 @@ PYBIND11_MODULE(flagser_count_pybind, m) { std::cout.rdbuf(nullptr); // Running flagser-count's count_cells routine - auto cell_count = count_cells(graph, named_arguments); + auto cell_count = count_cells(graph, params); // Re-enable again cout std::cout.rdbuf(cout_buff);