diff --git a/.github/workflows/custom-branch.yml b/.github/workflows/custom-branch.yml index 250ddde2..467aca80 100644 --- a/.github/workflows/custom-branch.yml +++ b/.github/workflows/custom-branch.yml @@ -60,7 +60,7 @@ jobs: run: | git clone --depth 1 --branch ${{ inputs.branch }} https://github.com/${{ inputs.fork }}.git cd libsemigroups - ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" --disable-hpcombi && sudo make install -j8 + ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" && sudo make install -j8 ccache -s # Build libsemigroups_pybind11 diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index b5d96a37..dbfecd81 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -51,7 +51,7 @@ jobs: run: | git clone --depth 1 --branch main https://github.com/libsemigroups/libsemigroups.git cd libsemigroups - ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" --disable-hpcombi && sudo make install -j8 + ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" && sudo make install -j8 ccache -s # Build libsemigroups_pybind11 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bc483402..d2b6b5aa 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -63,7 +63,7 @@ jobs: run: | git clone --depth 1 --branch main https://github.com/libsemigroups/libsemigroups.git cd libsemigroups - ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" --disable-hpcombi && sudo make install -j8 + ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" && sudo make install -j8 ccache -s # Build libsemigroups_pybind11 diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 4ca4915c..08cf8871 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -48,7 +48,7 @@ jobs: run: | git clone --depth 1 --branch main https://github.com/libsemigroups/libsemigroups.git cd libsemigroups - ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" --disable-hpcombi && sudo make install -j8 + ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" && sudo make install -j8 ccache -s # Build libsemigroups_pybind11 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index da97df40..e1317261 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,7 +50,7 @@ jobs: run: | git clone --depth 1 --branch main https://github.com/libsemigroups/libsemigroups.git cd libsemigroups - ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" --disable-hpcombi && sudo make install -j8 + ./autogen.sh && ./configure CXX="$CXX" CXXFLAGS="$CXXFLAGS" && sudo make install -j8 ccache -s # Build libsemigroups_pybind11 diff --git a/.gitignore b/.gitignore index 6fd390e7..20656371 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,21 @@ -_build -_generate -.cache -.ccls -.coverage -.DS_Store +*.aux *.ccls-cache *.egg-info *.gv +*.log *.pdf *.py[cod] *.so *.whl +.DS_Store +.cache +.ccls +.coverage +MANIFEST +_build +_generate build coverage.xml gh-pages/ htmlcov -MANIFEST src/libsemigroups_pybind11/_version.py diff --git a/docs/source/conf.py b/docs/source/conf.py index ed2df21c..d85408a4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -193,7 +193,7 @@ ############ intersphinx ############ -# Thhe locations and names of other projects that should be linked to in this +# The locations and names of other projects that should be linked to in this # documentation. intersphinx_mapping = { "python": ("https://docs.python.org/3", None), diff --git a/docs/source/data-structures/elements/transformations/index.rst b/docs/source/data-structures/elements/transformations/index.rst index 8a080d75..59699bc6 100644 --- a/docs/source/data-structures/elements/transformations/index.rst +++ b/docs/source/data-structures/elements/transformations/index.rst @@ -18,3 +18,8 @@ This page describes the functionality for various partial transformations in pperm transf helpers + +.. seealso:: + + For high performance combinatorics on partial transformations of up to 16 + points, see :doc:`/data-structures/hpcombi/index`. diff --git a/docs/source/data-structures/hpcombi/index.rst b/docs/source/data-structures/hpcombi/index.rst new file mode 100644 index 00000000..36ef8eb2 --- /dev/null +++ b/docs/source/data-structures/hpcombi/index.rst @@ -0,0 +1,49 @@ +.. + Copyright (c) 2021-2024 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +HPCombi +======= + +This page describes the functionality from `HPCombi`_ available in +``libsemigroups_pybind11``. This functionality is only available if +:any:`LIBSEMIGROUPS_HPCOMBI_ENABLED` is ``True``. `HPCombi`_ provides high +performance (SIMD accelerated) partial transformations, transformations, +permutations, and partial permutations on up to ``16`` points. The classes +implementing these element types belong to the ``hpcombi`` subpackage of +``libsemigroups_pybind11``, and can be used with the ``libsemigroups_pybind11`` +classes: + +* :any:`FroidurePin` +* :any:`Konieczny` +* :any:`SchreierSims` + +Variables +~~~~~~~~~ + +.. py:attribute:: LIBSEMIGROUPS_HPCOMBI_ENABLED + :type: bool + + This variable indicates whether or not the version of ``libsemigroups`` + being used by ``libsemigroups_pybind11`` was compiled with `HPCombi`_ + enabled. + +Classes +~~~~~~~ + +.. toctree:: + :maxdepth: 1 + + perm16 + pperm16 + ptransf16 + transf16 + vect16 + + +.. _HPCombi: https://libsemigroups.github.io/HPCombi/ diff --git a/docs/source/data-structures/hpcombi/perm16.rst b/docs/source/data-structures/hpcombi/perm16.rst new file mode 100644 index 00000000..f0fb7822 --- /dev/null +++ b/docs/source/data-structures/hpcombi/perm16.rst @@ -0,0 +1,56 @@ +.. + Copyright (c) 2025 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11.hpcombi + +The Perm16 class +================ + +.. autoclass:: Perm16 + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~Perm16 + Perm16.copy + Perm16.cycles_partition + Perm16.elementary_transposition + Perm16.inverse + Perm16.inverse_arr + Perm16.inverse_cycl + Perm16.inverse_find + Perm16.inverse_pow + Perm16.inverse_ref + Perm16.inverse_sort + Perm16.left_weak_leq + Perm16.left_weak_leq_length + Perm16.left_weak_leq_ref + Perm16.lehmer + Perm16.lehmer_arr + Perm16.lehmer_ref + Perm16.length + Perm16.length_arr + Perm16.length_ref + Perm16.nb_cycles + Perm16.nb_cycles_ref + Perm16.nb_cycles_unroll + Perm16.nb_descents + Perm16.nb_descents_ref + Perm16.one + Perm16.unrankSJT + Perm16.validate + +Full API +-------- + +.. autoclass:: Perm16 + :class-doc-from: init + :members: diff --git a/docs/source/data-structures/hpcombi/pperm16.rst b/docs/source/data-structures/hpcombi/pperm16.rst new file mode 100644 index 00000000..31ce2a0e --- /dev/null +++ b/docs/source/data-structures/hpcombi/pperm16.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2025 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11.hpcombi + +The PPerm16 class +================= + +.. autoclass:: PPerm16 + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~PPerm16 + PPerm16.copy + PPerm16.inverse_ref + PPerm16.left_one + PPerm16.one + PPerm16.right_one + PPerm16.validate + +Full API +-------- + +.. autoclass:: PPerm16 + :class-doc-from: init + :members: diff --git a/docs/source/data-structures/hpcombi/ptransf16.rst b/docs/source/data-structures/hpcombi/ptransf16.rst new file mode 100644 index 00000000..f789db29 --- /dev/null +++ b/docs/source/data-structures/hpcombi/ptransf16.rst @@ -0,0 +1,49 @@ +.. + Copyright (c) 2025 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11.hpcombi + +The PTransf16 class +=================== + +.. autoclass:: PTransf16 + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~PTransf16 + PTransf16.copy + PTransf16.domain_bitset + PTransf16.domain_mask + PTransf16.fix_points_bitset + PTransf16.fix_points_mask + PTransf16.image_bitset + PTransf16.image_mask + PTransf16.image_mask_ref + PTransf16.largest_fix_point + PTransf16.largest_moved_point + PTransf16.left_one + PTransf16.nb_fix_points + PTransf16.one + PTransf16.rank + PTransf16.rank_cmpestrm + PTransf16.rank_ref + PTransf16.right_one + PTransf16.smallest_fix_point + PTransf16.smallest_moved_point + PTransf16.validate + +Full API +-------- + +.. autoclass:: PTransf16 + :class-doc-from: init + :members: diff --git a/docs/source/data-structures/hpcombi/transf16.rst b/docs/source/data-structures/hpcombi/transf16.rst new file mode 100644 index 00000000..dd72e56a --- /dev/null +++ b/docs/source/data-structures/hpcombi/transf16.rst @@ -0,0 +1,32 @@ +.. + Copyright (c) 2025 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11.hpcombi + +The Transf16 class +================== + +.. autoclass:: Transf16 + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~Transf16 + Transf16.copy + Transf16.one + Transf16.validate + +Full API +-------- + +.. autoclass:: Transf16 + :class-doc-from: init + :members: diff --git a/docs/source/data-structures/hpcombi/vect16.rst b/docs/source/data-structures/hpcombi/vect16.rst new file mode 100644 index 00000000..f20d3fe2 --- /dev/null +++ b/docs/source/data-structures/hpcombi/vect16.rst @@ -0,0 +1,41 @@ +.. + Copyright (c) 2025 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11.hpcombi + +The Vect16 class +================ + +.. autoclass:: Vect16 + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~Vect16 + Vect16.copy + Vect16.eval16 + Vect16.first_diff + Vect16.first_non_zero + Vect16.first_zero + Vect16.is_permutation + Vect16.last_diff + Vect16.last_non_zero + Vect16.last_zero + Vect16.less_partial + Vect16.partial_sums + Vect16.sum + +Full API +-------- + +.. autoclass:: Vect16 + :class-doc-from: init + :members: diff --git a/docs/source/index.rst b/docs/source/index.rst index 98f4779d..6442b387 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -139,6 +139,7 @@ We would like to thank the authors and contributors of these projects! data-structures/constants/index data-structures/elements/index data-structures/enums/index + data-structures/hpcombi/index data-structures/order/index data-structures/presentations/index data-structures/suffix-trees/index diff --git a/setup.py b/setup.py index c647d928..0cff1726 100644 --- a/setup.py +++ b/setup.py @@ -11,10 +11,17 @@ import glob import os +import platform import sys import pkgconfig -from pybind11.setup_helpers import ParallelCompile, Pybind11Extension, build_ext, naive_recompile +from pybind11.setup_helpers import ( + ParallelCompile, + Pybind11Extension, + build_ext, + has_flag, + naive_recompile, +) from setuptools import setup ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() @@ -41,6 +48,49 @@ print("Library directories args are:") print(libsemigroups_info["library_dirs"]) + +def get_arch(): + """Simple function to return the architecture, namely, x86 or not""" + arch = platform.machine().lower() + if arch in {"x86_64", "amd64", "i386", "i686"}: + return "x86" + if arch in {"arm64", "aarch64", "armv7l", "armv6l"}: + return "arm" + return arch + + +class LibsemigroupsBuildExt(build_ext): + # pylint: disable=too-few-public-methods + """Class conditionally add compile flags""" + + def build_extensions(self): + """Adds compile flags before calling build_extensions in build_ext""" + compiler = self.compiler + + if has_flag(compiler, "-mavx"): + print("Compiler supports '-mavx' flag, adding it to 'extra_compile_args'") + for ext in self.extensions: + ext.extra_compile_args += ["-mavx"] + else: + print("Compiler does not support '-mavx' flag, not adding it to 'extra_compile_args'") + if get_arch() == "arm" and ( + any(x.startswith("gcc") for x in compiler.compiler) + or any(x.startswith("g++") for x in compiler.compiler_cxx) + ): + print( + "Compiler is gcc, and architecture is arm, adding '-fpermissive' to " + "'extra_compile_args'" + ) + for ext in self.extensions: + ext.extra_compile_args += ["-fpermissive"] + + for ext in self.extensions: + print(f"'extra_compile_args' for '{ext.name}' are:") + print(ext.extra_compile_args) + + super().build_extensions() + + ext_modules = [ Pybind11Extension( "_libsemigroups_pybind11", @@ -49,7 +99,8 @@ library_dirs=libsemigroups_info["library_dirs"], language="c++", libraries=["semigroups"], + extra_compile_args=["-flax-vector-conversions"], ) ] -setup(ext_modules=ext_modules, cmdclass={"build_ext": build_ext}) +setup(ext_modules=ext_modules, cmdclass={"build_ext": LibsemigroupsBuildExt}) diff --git a/src/froidure-pin.cpp b/src/froidure-pin.cpp index 9c177a9b..ea427bd0 100644 --- a/src/froidure-pin.cpp +++ b/src/froidure-pin.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1268,20 +1269,17 @@ This function returns the element of *fp* obtained by evaluating *w*. }); } } // bind_froidure_pin_stateful - } // namespace + } // namespace void init_froidure_pin(py::module& m) { - // TODO(0) uncomment bind_froidure_pin>(m, "Transf16"); bind_froidure_pin_stateless>(m, "Transf1"); bind_froidure_pin_stateless>(m, "Transf2"); bind_froidure_pin_stateless>(m, "Transf4"); - // TODO(0) uncomment bind_froidure_pin_stateless>(m, - // "PPerm16"); + bind_froidure_pin_stateless>(m, "PPerm1"); bind_froidure_pin_stateless>(m, "PPerm2"); bind_froidure_pin_stateless>(m, "PPerm4"); - // TODO(0) uncomment bind_froidure_pin_stateless>(m, - // "Perm16"); + bind_froidure_pin_stateless>(m, "Perm1"); bind_froidure_pin_stateless>(m, "Perm2"); bind_froidure_pin_stateless>(m, "Perm4"); @@ -1322,5 +1320,12 @@ This function returns the element of *fp* obtained by evaluating *w*. m, "KEWord"); // codespell:ignore keword bind_froidure_pin_stateful(m, "TCE"); + +#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + bind_froidure_pin_stateless(m, "HPCombiPPerm16"); + bind_froidure_pin_stateless(m, "HPCombiPTransf16"); + bind_froidure_pin_stateless(m, "HPCombiPerm16"); + bind_froidure_pin_stateless(m, "HPCombiTransf16"); +#endif } } // namespace libsemigroups diff --git a/src/hpcombi.cpp b/src/hpcombi.cpp new file mode 100644 index 00000000..4aa2399d --- /dev/null +++ b/src/hpcombi.cpp @@ -0,0 +1,2391 @@ +// +// libsemigroups_pybind11 +// Copyright (C) 2025 James D. Mitchell +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// libsemigroups headers +#include + +#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + +// pybind11.... +#include +#include +#include + +// libsemigroups_pybind11.... +#include "main.hpp" // for init_hpcombi + +namespace py = pybind11; + +namespace HPCombi { + namespace { + using LibsemigroupsException = ::libsemigroups::LibsemigroupsException; + + std::string repr(Vect16 const& self, std::string_view prefix) { + auto result = std::to_string(self); + result[0] = '['; + result.back() = ']'; + return fmt::format("{}({})", prefix, result); + } + + //////////////////////////////////////////////////////////////////////// + // Vect16 + //////////////////////////////////////////////////////////////////////// + + void init_hpcombi_vect16(py::module& m) { + py::class_ thing(m, + "hpcombi_Vect16", + R"pbdoc( +Vector of ``16`` bytes, with some SIMD optimized methods, superclass of +:any:`hpcombi.Transf16`. Entries in :any:`Vect16` must be integers in the range :math:`[0, 256)`. + +This class belongs to the ``hpcombi`` subpackage of ``libsemigroups_pybind11``. + +The functionality described on this page is only available if +:any:`LIBSEMIGROUPS_HPCOMBI_ENABLED` is ``True``. +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Special methods + //////////////////////////////////////////////////////////////////////// + + thing.def("__repr__", + [](Vect16 const& thing) { return repr(thing, "Vect16"); }); + + thing.def( + "__getitem__", + [](Vect16& self, size_t i) { + if (i > 15) { + throw std::out_of_range(fmt::format( + "index out of range, expected value in [0, 16), found {}", + i)); + } + return self[i]; + }, + py::arg("i"), + py::is_operator()); + + thing.def( + "__setitem__", + [](Vect16& x, size_t i, size_t val) { + if (i > 15) { + throw std::out_of_range(fmt::format( + "index out of range, expected value in [0, 16), found {}", + i)); + } + x[i] = val; + }, + py::is_operator()); + + thing.def("__len__", [](Vect16 const&) { return Vect16::size(); }); + + thing.def("__copy__", [](Vect16 const& v) { return Vect16(v); }); + + thing.def("__hash__", + [](Vect16 const& v) { return std::hash{}(v); }); + + //////////////////////////////////////////////////////////////////////// + // Operators + //////////////////////////////////////////////////////////////////////// + + thing.def(py::self == py::self); + thing.def(py::self != py::self); + thing.def(py::self < py::self); + + // NOTE: size isn't bound, use "len" instead + + //////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////// + + thing.def(py::init<>(), R"pbdoc( +Default constructor. + +Constructs a :any:`Vect16` object with its entries uninitialized. This means there +is no guarantee about the values in the constructed object. +)pbdoc"); + + thing.def(py::init([](std::vector img) { + if (img.size() < 16) { + img.resize(16, 0); + } else if (img.size() > 16) { + LIBSEMIGROUPS_EXCEPTION( + "expected a list of at most 16 values, found {}", + img.size()); + } + return Vect16({img[0], + img[1], + img[2], + img[3], + img[4], + img[5], + img[6], + img[7], + img[8], + img[9], + img[10], + img[11], + img[12], + img[13], + img[14], + img[15]}); + }), + R"pbdoc( +:sig=(self: Vect16, img: list[int]) -> None: + +Construct a :any:`Vect16` from a list of its entries. + +This function constructs a :any:`Vect16` from the list *img* of its entries. +If the length of *img* is less than ``16``, then the constructed :any:`Vect16` +is padded with ``0`` values at the end. + +:param img: The list of images. +:type img: list[int] + +:raises LibsemigroupsError: if any value in *img* exceeds ``255``. +:raises LibsemigroupsError: if the length of *img* exceeds ``16``. +:raises TypeError: if any value in *img* is larger than ``255``. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]) + Vect16([ 1, 2, 3, 4,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Other methods + //////////////////////////////////////////////////////////////////////// + + // NOTE: the following doesn't compile, so skipping + + // thing.def("as_array", + // &Vect16::as_array, + // R"pbdoc()pbdoc"); + + thing.def( + "copy", + [](Vect16 const& v) { return Vect16(v); }, + R"pbdoc( +:sig=(self: Vect16) -> Vect16: + +Copy a :any:`Vect16`. + +:returns: A copy of the argument. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> x = Vect16([1, 2, 3, 4, 255]) + >>> x.copy() is not x + True + >>> x.copy() == x + True +)pbdoc"); + + thing.def("first_diff", + &Vect16::first_diff, + py::arg("other"), + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, other: Vect16, bound: int = 16) -> int: + +Returns the position of the first diff. + +This function returns the first diff in *self* and *other* among the first +*bound* entries. That is, the minimum ``i`` such that ``self[i] != other[i]`` +where ``i`` is in the range from ``0`` to ``bound - 1``. If *self* and *other* +agree up to position ``bound - 1``, then ``16`` is returned. + +:param other: The vector for comparison. +:type other: Vect16 + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The position of the first difference or ``16``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]).first_diff(Vect16([1, 2, 3, 4, 245])) + 4 +)pbdoc"); + + thing.def("last_diff", + &Vect16::last_diff, + py::arg("other"), + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, other: Vect16, bound: int = 16) -> int: + +Returns the position of the last diff. + +This function returns the last diff in *self* and *other* among the first +*bound* entries. That is, the maximum ``i`` such that ``self[i] != other[i]`` +where ``i`` is in the range from ``0`` to ``bound - 1``. If *self* and *other* +agree up to position ``bound - 1``, then ``16`` is returned. + +:param other: The vector for comparison. +:type other: Vect16 + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The position of the last difference or ``16``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]).last_diff(Vect16([1, 2, 3, 4, 245])) + 4 +)pbdoc"); + + thing.def("first_zero", + &Vect16::first_zero, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, bound: int = 16) -> int: + +Returns the position of the first zero. + +This function returns the first zero in *self* among the first *bound* entries. +That is, the minimum ``i`` such that ``self[i] == 0`` where ``i`` is in the +range from ``0`` to ``bound - 1``. If *self* contains no zeros, then ``16`` is +returned. + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The position of the first zero or ``16``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]).first_zero() + 5 + >>> Vect16().first_zero() + 0 + >>> Vect16([1, 2, 3, 4, 255]).first_zero(3) + 16 +)pbdoc"); + + thing.def("last_zero", + &Vect16::last_zero, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, bound: int = 16) -> int: + +Returns the position of the last zero. + +This function returns the last zero in *self* among the last *bound* entries. +That is, the maximum ``i`` such that ``self[i] == 0`` where ``i`` is in the +range from ``0`` to ``bound - 1``. If *self* contains no zeros, then ``16`` is +returned. + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The position of the last zero or ``16``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]).last_zero() + 15 + >>> Vect16([1, 2, 3, 4, 255]).first_zero(3) + 16 + >>> Vect16().last_zero() + 15 +)pbdoc"); + + thing.def("first_non_zero", + &Vect16::first_non_zero, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, bound: int = 16) -> int: + +Returns the position of the first non-zero item. + +This function returns the first non-zero item in *self* among the first *bound* entries. +That is, the minimum ``i`` such that ``self[i] != 0`` where ``i`` is in the +range from ``0`` to ``bound - 1``. If *self* contains no non-zero items, then ``16`` is +returned. + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The position of the first zero or ``16``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]).first_non_zero() + 0 + >>> Vect16().first_non_zero() + 16 +)pbdoc"); + + thing.def("last_non_zero", + &Vect16::last_non_zero, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, bound: int = 16) -> int: + +Returns the position of the first non-zero item. + +This function returns the first non-zero item in *self* among the first *bound* entries. +That is, the minimum ``i`` such that ``self[i] != 0`` where ``i`` is in the +range from ``0`` to ``bound - 1``. If *self* contains no non-zero items, then ``16`` is +returned. + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The position of the first zero or ``16``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([1, 2, 3, 4, 255]).last_non_zero() + 4 + >>> Vect16([1, 2, 3, 4, 255]).last_non_zero(3) + 2 + >>> Vect16().last_non_zero() + 16 +)pbdoc"); + + // NOTE: This seems unnecessary, see the test file, we can + // already do list(x) and [x for x in v] etc, so no need for + // this. + + // thing.def( + // "iterator", + // [](Vect16 const& self) { + // return py::make_iterator(self.cbegin(), + // self.cend()); + // }, + // R"pbdoc( + // )pbdoc"); + + thing.def("less_partial", + &Vect16::less_partial, + py::arg("other"), + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, other: Vect16, bound: int = 16) -> int: + +Returns the difference of the first diff. + +This function returns the first non-zero difference (if any) between in +``self[i]`` and ``other[i]`` among the first *bound* entries or ``0``. + +:param other: The vector for comparison. +:type other: Vect16 + +:param bound: The bound (defaults to ``16``). +:type bound: int + +:returns: The difference or ``0``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> v = Vect16([0, 1, 2, 3]) + >>> u = Vect16([0, 1, 2, 10]) + >>> v.less_partial(u, 3) + 0 + >>> v.less_partial(u, 4) + -7 + >>> u.less_partial(v, 4) + 7 + >>> u.less_partial(v, 16) + 7 + >>> v.less_partial(u, 16) + -7 +)pbdoc"); + + // NOTE: the following function is named badly, and I'm not + // completely sure what it does when the values in "other" are > + // 15, so omitting for now. + + // thing.def("permuted", + // &Vect16::permuted, + // py::arg("other"), + // R"pbdoc( + // )pbdoc"); + + thing.def("sum", + &Vect16::sum, + R"pbdoc( +:sig=(self: Vect16) -> int: + +Returns the sum of the entries mod ``256``. + +This function returns the sum of the items in *self* mod ``256``. + +:returns: The sum of the items in *self* mod ``256``. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([0, 1, 2, 3]).sum() + 6 + >>> Vect16([0, 1, 2, 10]).sum() + 13 + >>> Vect16([1, 2, 3, 255]).sum() + 5 +)pbdoc"); + + thing.def("partial_sums", + &Vect16::partial_sums, + R"pbdoc( +:sig=(self: Vect16) -> Vect16: + +Returns the :any:`Vect16` of partial sums of the entries mod ``256``. + +This function returns the :any:`Vect16` of partial sums of the items in *self* +mod ``256``. + +:returns: The partial sums of the items in *self* mod ``256``. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([0, 1, 2, 3]).partial_sums() + Vect16([ 0, 1, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6]) + >>> Vect16([1, 2, 3, 255]).partial_sums() + Vect16([ 1, 3, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]) +)pbdoc"); + + thing.def("eval16", + &Vect16::eval16, + R"pbdoc( +:sig=(self: Vect16) -> Vect16: + +Counts how many times each value in :math:`[0, 16)` appears in *self*. + +This function counts how many times each value in :math:`[0, 16)` appears in +*self*. In other words, this function returns a :any:`Vect16` such that the +item in position ``i`` is the number of occurrences of ``i`` in *self*. + +:returns: The counts. +:rtype: Vect16 + +.. warning: Values in *self* larger than ``15`` are ignored. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([5, 5, 2, 5, 1, 6,12, 4, 0, 3, 2,11,12,13,14,15]).eval16() + Vect16([ 1, 1, 2, 1, 1, 3, 1, 0, 0, 0, 0, 1, 2, 1, 1, 1]) + +)pbdoc"); + + thing.def( + "is_permutation", + [](Vect16 const& self, size_t bound) { + return self.is_permutation(bound); + }, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Vect16, bound: int = 16) -> bool: + +Returns whether or not the vector defines a permutation. + +This function returns ``True`` if the first *bound* entries of *self* +define a permutation; and ``False`` otherwise. + +:param bound: The number of entries to check (defaults to ``16``). +:type bound: int + +:returns: Whether or not *self* is a permutation of its first *bound* entries. +:rtype: bool + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> Vect16([5, 5, 2, 5, 1, 6,12, 4, 0, 3, 2,11,12,13,14,15]).is_permutation() + False + >>> Vect16([1, 0, 2, 3, 4, 4]).is_permutation() + False + >>> Vect16([1, 0, 2, 3, 4] + list(range(5, 16))).is_permutation() + True +)pbdoc"); + } // init_hpcombi_vect16 + + //////////////////////////////////////////////////////////////////////// + // PTransf16 + //////////////////////////////////////////////////////////////////////// + + void init_hpcombi_ptransf16(py::module& m) { + py::class_ thing(m, + "hpcombi_PTransf16", + R"pbdoc( +Class representing partial transformations. + +SIMD accelerated class :any:`PTransf16` representing partial transformations on +up to ``16`` points. Partial means it might not be defined everywhere. +Undefined images are encoded as ``255``. + +This class belongs to the ``hpcombi`` subpackage of ``libsemigroups_pybind11``. + +The functionality described on this page is only available if +:any:`LIBSEMIGROUPS_HPCOMBI_ENABLED` is ``True``. + +:any:`PTransf16` inherits from :any:`Vect16`. +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Special methods + //////////////////////////////////////////////////////////////////////// + + thing.def("__repr__", + [](PTransf16 const& self) { return repr(self, "PTransf16"); }); + + thing.def( + "__mul__", + // The next line is not a type, but is consistent with the + // other transformations in libsemigroups_pybind11, since + // function composition in HPCombi is backwards. + [](PTransf16 const& x, PTransf16 const& y) { return y * x; }, + py::is_operator()); + + thing.def("__copy__", [](PTransf16 const& v) { return PTransf16(v); }); + + //////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////// + + thing.def(py::init<>(), R"pbdoc( +:sig=(self: PTransf16) -> None: + +Default constructor. + +Constructs a :any:`PTransf16` object with its entries uninitialized. This means +there is no guarantee about the values in the constructed object. +)pbdoc"); + + thing.def(py::init([](std::vector img) { + return ::libsemigroups::make(std::move(img)); + }), + R"pbdoc( +:sig=(self: PTransf16, img: list[int]) -> None: + +Construct a :any:`PTransf16` from a list of images. + +This function constructs a :any:`PTransf16` from the list *img* of its entries. +If the length of *img* is less than ``16``, then the constructed :any:`PTransf16` +is fixed points at the end. + +:param img: The list of images. +:type img: list[int] + +:raises LibsemigroupsError: if any value in *img* exceeds ``16`` and is not equal to ``255``. +:raises LibsemigroupsError: if the length of *img* exceeds ``16``. +:raises TypeError: if any value in *img* is larger than ``255``. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 255, 1, 10]) + PTransf16([ 1,255, 1,10, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def( // Seems like the last arg does nothing + py::init([](std::vector dom, std::vector ran) { + return ::libsemigroups::make(dom, ran, 16); + }), + R"pbdoc( +:sig=(self: PTransf16, dom: list[int], im: list[int]) -> None: + +Construct from domain and image. + +Constructs a partial transformation of degree *n* such that ``(dom[i])f = +im[i]`` for all ``i`` and which is undefined (``255`` represents undefined in +this context) on every other value in the range :math:`[0, n)`. + +:param dom: the domain. +:type dom: list[int] +:param im: the image. +:type im: list[int] + +:raises LibsemigroupsError: *dom* and *im* do not have the same size. +:raises LibsemigroupsError: any value in *dom* or *im* is greater than *15*. +:raises LibsemigroupsError: there are repeated entries in *dom*. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 2], [3, 4]) + PTransf16([255, 3, 4,255,255,255,255,255,255,255,255,255,255,255,255,255]) +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Static methods + //////////////////////////////////////////////////////////////////////// + + thing.def_static("one", + &PTransf16::one, + R"pbdoc( +:sig=one() -> PTransf16: + +Returns the identity partial transformation. + +This function returns the identity :any:`PTransf16` which fixes every value in +:math:`[0, 16)`. + +:returns: The identity transformation. +:rtype: PTransf16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 0, 1]) + >>> x * x.one() == PTransf16.one() * x == x + True +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Other methods + //////////////////////////////////////////////////////////////////////// + + thing.def( + "copy", + [](PTransf16 const& v) { return PTransf16(v); }, + R"pbdoc( +:sig=(self: PTransf16) -> PTransf16: + +Copy a :any:`PTransf16`. + +:returns: A copy of the argument. +:rtype: PTransf16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 2, 3, 4, 255]) + >>> x.copy() is not x + True + >>> x.copy() == x + True +)pbdoc"); + + thing.def("validate", + &PTransf16::validate, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: PTransf16, bound: int = 16) -> bool: + +Check whether or not a :any:`PTransf16` is well-defined. + +This function returns ``True`` if *self* is a well-defined partial +transformation (i.e. no image value is larger than ``15``) on the values +``0`` up to *bound*. + +:param bound: the bound (defaults to ``16``). +:type bound: int + +:returns: Whether or not *self* is valid. +:rtype: bool + +.. note:: + It should not be possible to create an invalid :any:`PTransf16` in + ``libsemigroups_pybind11``, and this function is only included for + completeness. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 0, 1]).validate() + True +)pbdoc"); + + // NOTE: the following doesn't compile and so is omitted. + + // thing.def("image_mask_cmpestrm", + // &PTransf16::image_mask_cmpestrm, + // py::arg("complement"), + // R"pbdoc( + // Returns a mask for the image of *this. + // )pbdoc"); + + thing.def( + "image_mask_ref", + [](PTransf16 const& self, bool complement) { + return Vect16(self.image_mask_ref(complement)); + }, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> Vect16: + +Returns a mask for the image. + +This function returns a mask for the image of *self* or its complement +depending on the value of *complement*. If *complement* is ``True``, then the +returned mask has ``0`` in position ``i`` for every ``i`` in the image of +*self* and ``255`` (undefined) otherwise. If *complement* is ``False``, then +the returned mask has ``0`` in position ``i`` for every ``i`` not in the image +of *self* and ``255`` otherwise. + +This is the reference implementation, use :any:`image_mask` for better performance. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The image mask or its complement. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 0, 1]).image_mask_ref(True) + Vect16([ 0, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + >>> PTransf16([1, 0, 1]).image_mask_ref(False) + Vect16([255,255, 0,255,255,255,255,255,255,255,255,255,255,255,255,255]) +)pbdoc"); + + thing.def( + "image_mask", + [](PTransf16 const& self, bool complement) { + return Vect16(self.image_mask(complement)); + }, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> Vect16: + +Returns a mask for the image. + +This function returns a mask for the image of *self* or its complement +depending on the value of *complement*. If *complement* is ``True``, then the +returned mask has ``0`` in position ``i`` for every ``i`` in the image of +*self* and ``255`` (undefined) otherwise. If *complement* is ``False``, then +the returned mask has ``0`` in position ``i`` for every ``i`` not in the image +of *self* and ``255`` otherwise. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The image mask or its complement. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 0, 1]).image_mask(True) + Vect16([ 0, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + >>> PTransf16([1, 0, 1]).image_mask(False) + Vect16([255,255, 0,255,255,255,255,255,255,255,255,255,255,255,255,255]) +)pbdoc"); + + thing.def("image_bitset", + &PTransf16::image_bitset, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> int: + +Returns a bit mask (as an ``int``) for the image of *self* (or its complement). + +This function returns a bitset mask for the image of *self* or its complement +depending on the value of *complement*. If *complement* is ``True``, then the +returned mask has ``1`` in bit ``i`` if and only if ``i`` is in the image of +*self*. If *complement* is ``False``, then the returned mask has ``0`` in bit +``i`` if and only if ``i`` is in the image of *self*. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The image bitset or its complement. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.image_bitset() + 65514 + >>> bin(x.image_bitset()) + '0b1111111111101010' + >>> bin(x.image_bitset(True)) + '0b10101' + >>> bool(x.image_bitset() & 1 << 1) + True + >>> bool(x.image_bitset() & 1 << 2) + False +)pbdoc"); + + thing.def( + "domain_mask", + [](PTransf16 const& self, bool complement) { + return Vect16(self.domain_mask(complement)); + }, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> Vect16: + +Returns a mask for the domain. + +This function returns a mask for the domain of *self* or its complement +depending on the value of *complement*. If *complement* is ``True``, then the +returned mask has ``0`` in position ``i`` for every ``i`` in the domain of +*self* and ``255`` (undefined) otherwise. If *complement* is ``False``, then +the returned mask has ``0`` in position ``i`` for every ``i`` not in the domain +of *self* and ``255`` otherwise. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The domain mask or its complement. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 0, 1]).domain_mask(True) + Vect16([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + >>> PTransf16([1, 0, 1]).domain_mask(False) + Vect16([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]) +)pbdoc"); + + thing.def("domain_bitset", + &PTransf16::domain_bitset, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> int: + +Returns a bit mask (as an ``int``) for the domain of *self* (or its complement). + +This function returns a bitset mask for the domain of *self* or its complement +depending on the value of *complement*. If *complement* is ``True``, then the +returned mask has ``1`` in bit ``i`` if and only if ``i`` is in the domain of +*self*. If *complement* is ``False``, then the returned mask has ``0`` in bit +``i`` if and only if ``i`` is in the domain of *self*. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The domain bitset or its complement. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.domain_bitset() + 65527 + >>> bin(x.domain_bitset()) + '0b1111111111110111' + >>> bin(x.domain_bitset(True)) + '0b1000' + >>> bool(x.domain_bitset() & 1 << 1) + True + >>> bool(x.domain_bitset() & 1 << 3) + False +)pbdoc"); + + thing.def("right_one", + // This is not a typo, everything is reversed in HPCombi + &PTransf16::left_one, + R"pbdoc( +:sig=(self: PTransf16) -> PTransf16: +Returns the right one of a partial transformation. + +This function returns a newly constructed :any:`PTransf16` with the same image as +*self* and that acts as the identity on *self* by right multiplication. + +:returns: A right one of *self*. +:rtype: PTransf16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.image_bitset() == x.right_one().image_bitset() + True + >>> x * x.right_one() == x + True +)pbdoc"); + + thing.def("left_one", + // This is not a typo, everything is reversed in HPCombi + &PTransf16::right_one, + R"pbdoc( +:sig=(self: PTransf16) -> PTransf16: + +Returns the left one of a partial transformation. + +This function returns a newly constructed :any:`PTransf16` with the same image as +*self* and that acts as the identity on *self* by left multiplication. + +:returns: A left one of *self*. +:rtype: PTransf16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.domain_bitset() == x.left_one().domain_bitset() + True + >>> x.left_one() * x == x + True +)pbdoc"); + + thing.def("rank_ref", + &PTransf16::rank_ref, + R"pbdoc( +:sig=(self: PTransf16) -> int: +Returns the size of the image set of a partial transformation. + +This function returns the size of the image set of *self*. + +:returns: The size of the image set. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.rank_ref() + 13 +)pbdoc"); + + thing.def("rank", + &PTransf16::rank, + R"pbdoc( +:sig=(self: PTransf16) -> int: +Returns the size of the image set of a partial transformation. + +This function returns the size of the image set of *self*. + +:returns: The size of the image set. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.rank() + 13 +)pbdoc"); + + thing.def("rank_cmpestrm", + &PTransf16::rank_cmpestrm, + R"pbdoc( +:sig=(self: PTransf16) -> int: + +Returns the size of the image set of a partial transformation. + +This function returns the size of the image set of *self*. + +:returns: The size of the image set. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.rank_cmpestrm() + 13 +)pbdoc"); + + thing.def( + "fix_points_mask", + [](PTransf16 const& self, bool complement) { + return Vect16(self.fix_points_mask(complement)); + }, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> Vect16: + +Returns a mask for the fixed points of a partial transformation. + +This function returns a mask for the fixed points of *self* or its complement +depending on the value of *complement*. If *complement* is ``True``, then the +returned mask has ``255`` in position ``i`` for every fixed point ``i`` of +*self* and ``0`` (undefined) otherwise. If *complement* is ``False``, then +``0`` and ``255`` are switched in the output. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The fixed points mask or its complement. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Vect16 + >>> x = PTransf16([1, 3, 1, 255, 10]) + >>> x.fix_points_mask() + Vect16([ 0, 0, 0, 0, 0,255,255,255,255,255,255,255,255,255,255,255]) + >>> x.fix_points_mask(True) + Vect16([255,255,255,255,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + >>> x.one().fix_points_mask() + Vect16([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]) + >>> x.one().fix_points_mask(True) + Vect16([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +)pbdoc"); + + thing.def("fix_points_bitset", + &PTransf16::fix_points_bitset, + py::arg("complement") = false, + R"pbdoc( +:sig=(self: PTransf16, complement: bool = False) -> int: + +Returns a bit mask (as an ``int``) for the fixed, or non-fixed, points of *self*. + +This function returns a bitset mask for the fixed points of *self* or the +non-fixed points of *self* depending on the value of *complement*. If +*complement* is ``True``, then the returned mask has ``1`` in bit ``i`` if and +only if ``i`` is fixed by *self*. If *complement* is ``False``, then the +returned mask has ``0`` in bit ``i`` if and only if ``i`` is fixed by *self*. + +:param complement: whether or not the complement is sought (defaults to ``False``). +:type complement: bool + +:returns: The fixed points bitset or its complement. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> x = PTransf16([1, 3, 2, 255, 10]) + >>> x.fix_points_bitset() + 65508 + >>> x.fix_points_bitset(False) + 65508 + >>> x.fix_points_bitset(True) + 27 + >>> bin(x.fix_points_bitset()) + '0b1111111111100100' + >>> bin(x.fix_points_bitset(True)) + '0b11011' +)pbdoc"); + + thing.def("smallest_fix_point", + &PTransf16::smallest_fix_point, + R"pbdoc( +:sig=(self: PTransf16) -> int: + +Returns the smallest fix point. + +This function returns the smallest integer ``i`` such that +``self[i] == i`` or ``255`` if ``self[i] != i`` for all ``i < 16``. + +:returns: The smallest fixed point. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 3, 2, 255, 10]).smallest_fix_point() + 2 +)pbdoc"); + + thing.def("smallest_moved_point", + &PTransf16::smallest_moved_point, + R"pbdoc( +:sig=(self: PTransf16) -> int: + +Returns the smallest moved point. + +This function returns the smallest integer ``i`` such that +``self[i] != i`` or ``255`` if ``self[i] == i`` for all ``i < 16``. + +:returns: The smallest moved point. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 3, 2, 255, 10]).smallest_moved_point() + 0 + >>> PTransf16.one().smallest_moved_point() + 255 +)pbdoc"); + + thing.def("largest_fix_point", + &PTransf16::largest_fix_point, + R"pbdoc( +:sig=(self: PTransf16) -> int: + +Returns the largest fix point. + +This function returns the largest integer ``i`` such that +``self[i] == i`` or ``255`` if ``self[i] != i`` for all ``i < 16``. + +:returns: The largest fixed point. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 3, 2, 255, 10]).largest_fix_point() + 15 + >>> PTransf16.one().largest_fix_point() + 15 +)pbdoc"); + + thing.def("largest_moved_point", + &PTransf16::largest_moved_point, + R"pbdoc( +:sig=(self: PTransf16) -> int: + +Returns the largest moved point. + +This function returns the largest integer ``i`` such that +``self[i] != i`` or ``255`` if ``self[i] == i`` for all ``i < 16``. + +:returns: The largest moved point. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 3, 2, 255, 10]).largest_moved_point() + 4 + >>> PTransf16.one().largest_moved_point() + 255 +)pbdoc"); + + thing.def("nb_fix_points", + &PTransf16::nb_fix_points, + R"pbdoc( +:sig=(self: PTransf16) -> int: + +Returns the number of fixed points. + +This function returns the number of integers ``i`` such that +``self[i] != i`` and ``i < 16``. + +:returns: The number of fixed points. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PTransf16 + >>> PTransf16([1, 3, 2, 255, 10]).nb_fix_points() + 12 + >>> PTransf16.one().nb_fix_points() + 16 +)pbdoc"); + } // init_hpcombi_ptransf16 + + //////////////////////////////////////////////////////////////////////// + // Transf16 + //////////////////////////////////////////////////////////////////////// + + void init_hpcombi_transf16(py::module& m) { + py::class_ thing(m, + "hpcombi_Transf16", + R"pbdoc( +Class representing transformations. + +SIMD accelerated class :any:`Transf16` representing transformations on +up to ``16`` points. + +This class belongs to the ``hpcombi`` subpackage of ``libsemigroups_pybind11``. + +The functionality described on this page is only available if +:any:`LIBSEMIGROUPS_HPCOMBI_ENABLED` is ``True``. + +:any:`Transf16` inherits from :any:`PTransf16`. +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Special methods + //////////////////////////////////////////////////////////////////////// + + thing.def("__repr__", + [](Transf16 const& self) { return repr(self, "Transf16"); }); + + thing.def("__int__", + [](Transf16 const& x) { return static_cast(x); }); + + thing.def("__copy__", [](Transf16 const& v) { return Transf16(v); }); + + //////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////// + + thing.def(py::init<>(), R"pbdoc( +:sig=(self: Transf16) -> None: + +Default constructor. + +Constructs a :any:`Transf16` object with its entries uninitialized. This means +there is no guarantee about the values in the constructed object. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Transf16 + >>> x = Transf16() +)pbdoc"); + + thing.def(py::init([](std::vector img) { + return ::libsemigroups::make(std::move(img)); + }), + R"pbdoc( +:sig=(self: Transf16, img: list[int]) -> None: + +Construct a :any:`Transf16` from a list of images. + +This function constructs a :any:`Transf16` from the list *img* of its entries. +If the length of *img* is less than ``16``, then the constructed :any:`Transf16` +is padded with fixed points at the end. + +:param img: The list of images. +:type img: list[int] + +:raises LibsemigroupsError: if any value in *img* is in the range :math:`[16, 256)`. +:raises LibsemigroupsError: if the length of *img* exceeds ``16``. +:raises TypeError: if any value in *img* is larger than ``255``. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Transf16 + >>> Transf16([1, 3, 1, 10]) + Transf16([ 1, 3, 1,10, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def(py::init(), R"pbdoc( +:sig=(self: Transf16, n: int) -> None: + +Construct a transformation from its 64 bits compressed. + +This function constructs a :any:`Transf16` from its integer representation *n*. + +:param n: the integer representation. +:type n: int + +:raises TypeError: if *n* is not in the range :math:`[0, 2 ^{64})`. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Transf16 + >>> Transf16(2 ** 64 - 1) + Transf16([15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15]) + >>> int(Transf16([15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15])) + 18446744073709551615 +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Other methods + //////////////////////////////////////////////////////////////////////// + + thing.def( + "copy", + [](Transf16 const& v) { return Transf16(v); }, + R"pbdoc( +:sig=(self: Transf16) -> Transf16: + +Copy a :any:`Transf16`. + +:returns: A copy of the argument. +:rtype: Transf16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Transf16 + >>> x = Transf16([1, 2, 3, 4, 4]) + >>> x.copy() is not x + True + >>> x.copy() == x + True +)pbdoc"); + + thing.def("validate", + &Transf16::validate, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Transf16, bound: int = 16) -> bool: + +Check whether or not a :any:`Transf16` is well-defined. + +This function returns ``True`` if *self* is a well-defined partial +transformation (i.e. no image value is larger than ``15``) on the values +``0`` up to *bound*. + +:param bound: the bound (defaults to ``16``). +:type bound: int + +:returns: Whether or not *self* is valid. +:rtype: bool + +.. note:: + It should not be possible to create an invalid :any:`Transf16` in + ``libsemigroups_pybind11``, and this function is only included for + completeness. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Transf16 + >>> Transf16([1, 0, 1]).validate() + True +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Static methods + //////////////////////////////////////////////////////////////////////// + + thing.def_static("one", + &Transf16::one, + R"pbdoc( +:sig=one() -> Transf16: + +Returns the identity partial transformation. + +This function returns the identity :any:`Transf16` which fixes every value in +:math:`[0, 16)`. + +:returns: The identity transformation. +:rtype: Transf16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Transf16 + >>> x = Transf16([1, 0, 1]) + >>> x * x.one() == Transf16.one() * x == x + True +)pbdoc"); + } // init_hpcombi_transf16 + + //////////////////////////////////////////////////////////////////////// + // Perm16 + //////////////////////////////////////////////////////////////////////// + + void init_hpcombi_perm16(py::module& m) { + py::class_ thing(m, + "hpcombi_Perm16", + R"pbdoc( +Class representing permutations. + +SIMD accelerated class :any:`Perm16` representing permutations on +up to ``16`` points. + +This class belongs to the ``hpcombi`` subpackage of ``libsemigroups_pybind11``. + +The functionality described on this page is only available if +:any:`LIBSEMIGROUPS_HPCOMBI_ENABLED` is ``True``. + +:any:`Perm16` inherits from :any:`Transf16`. +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Special methods + //////////////////////////////////////////////////////////////////////// + + thing.def("__copy__", [](Perm16 const& self) { return Perm16(self); }); + + thing.def("__int__", + [](Perm16 const& x) { return static_cast(x); }); + + thing.def("__pow__", [](Perm16 const& self, int n) { + if (n == -1) { + return self.inverse(); + } + // TODO implement + throw pybind11::type_error("unsupported operand type(s) ** or pow(): " + "'Perm16' and 'int"); + }); + + thing.def("__repr__", + [](Perm16 const& self) { return repr(self, "Perm16"); }); + + //////////////////////////////////////////////////////////////////////// + // Static methods + //////////////////////////////////////////////////////////////////////// + + thing.def_static("one", + &Perm16::one, + R"pbdoc( +:sig=one() -> Perm16: + +Returns the identity permutation. + +This function returns the identity :any:`Perm16` which fixes every value in +:math:`[0, 16)`. + +:returns: The identity permutation. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([1, 2, 0]) + >>> x * x.one() == Perm16.one() * x == x + True +)pbdoc"); + + thing.def_static( + "elementary_transposition", + [](size_t i) { + if (i >= 15) { + LIBSEMIGROUPS_EXCEPTION("argument out of range, expected a value " + "in [0, 16) but found {}", + i); + } + return Perm16::elementary_transposition(i); + }, + py::arg("i"), + R"pbdoc( +:sig=elementary_transposition(i: int) -> Perm16: + +Returns the elementary transposition swapping *i* and *i* + 1. + +This function returns the elementary transposition swapping *i* and *i* + 1. +for every value *i* in :math:`[0, 15)`. + +:param i: the minimum value to be transposed. +:type i: int + +:returns: The transposition :math:`(i\ i + 1)`. +:rtype: Perm16 + +:raises LibsemigroupsError: + if any value in *i* is greater than or equal to ``15``. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16.elementary_transposition(2) + Perm16([ 0, 1, 3, 2, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def_static( + "unrankSJT", + // If anything other than 16 is passed as argument to unrankSJT, then + // the returned "permutation" is padding with trailing 0s and so not + // a permutation. Hence skipping this arg. + [](uint64_t r) { return Perm16::unrankSJT(16, r); }, + py::arg("r"), + R"pbdoc( +:sig=unrankSJT(r: int) -> Perm16: + +Returns the *r*-th permutation of size ``16`` in the Steinhaus–Johnson–Trotter +order. + +This function returns the *r*-th permutation of size ``16`` in the +Steinhaus–Johnson–Trotter order. + +:param r: The index. +:type r: int + +:returns: The *r*-th permutation. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16.unrankSJT(2) + Perm16([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,15,13,14]) +)pbdoc"); + + // TODO implement when + // https://github.com/libsemigroups/HPCombi/issues/60 + // is resolved. + // thing.def_static("random", + // &Perm16::random, + // py::arg("bound") = 16, + // R"pbdoc( + // A random permutation on ``0`` up to . + // )pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////// + + thing.def(py::init<>(), R"pbdoc( +:sig=(self: Perm16) -> None: + +Default constructor. + +Constructs a :any:`Perm16` object with its entries uninitialized. This means +there is no guarantee about the values in the constructed object. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16() +)pbdoc"); + + thing.def(py::init([](std::vector img) { + return ::libsemigroups::make(std::move(img)); + }), + R"pbdoc( +:sig=(self: Perm16, img: list[int]) -> None: + +Construct a :any:`Perm16` from a list of images. + +This function constructs a :any:`Perm16` from the list *img* of its entries. +If the length of *img* is less than ``16``, then the constructed :any:`Perm16` +is padded with fixed points at the end. + +:param img: The list of images. +:type img: list[int] + +:raises LibsemigroupsError: if any value in *img* is in the range :math:`[16, 256)`. +:raises LibsemigroupsError: if the length of *img* exceeds ``16``. +:raises LibsemigroupsError: if there are repeated values in *img*. +:raises TypeError: if any value in *img* is larger than ``255``. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([1, 3, 2, 0]) + Perm16([ 1, 3, 2, 0, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def(py::init(), R"pbdoc( +:sig=(self: Perm16, n: int) -> None: + +Construct a permutation from its 64 bits compressed. + +This function constructs a :any:`Perm16` from its integer representation *n*. + +:param n: the integer representation. +:type n: int + +:raises TypeError: if *n* is not in the range :math:`[0, 2 ^{64})`. + +.. warning:: + For most values of *n* the returned object is not a valid permutation. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16(17863200008537477760) + Perm16([ 0, 2, 1, 4, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) + >>> int(Perm16([0, 2, 1, 4, 3])) + 17863200008537477760 +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Other methods + //////////////////////////////////////////////////////////////////////// + + thing.def( + "copy", + [](Perm16 const& v) { return Perm16(v); }, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Copy a :any:`Perm16`. + +:returns: A copy of the argument. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([1, 2, 3, 4, 0]) + >>> x.copy() is not x + True + >>> x.copy() == x + True +)pbdoc"); + + thing.def("validate", + &Perm16::validate, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: Perm16, bound: int = 16) -> bool: + +Check whether or not a :any:`Perm16` is well-defined. + +This function returns ``True`` if *self* is a well-defined permutation +(i.e. no image value is larger than ``15`` and +no repeated image value) on the values ``0`` up to *bound*. + +:param bound: the bound (defaults to ``16``). +:type bound: int + +:returns: Whether or not *self* is valid. +:rtype: bool + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([1, 0, 2]).validate() + True + >>> x = Perm16([1, 0, 2]) + >>> x[0] = 0 + >>> x.validate() + False +)pbdoc"); + + thing.def("inverse", + &Perm16::inverse, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) + >>> x ** -1 + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("inverse_ref", + &Perm16::inverse_ref, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. This function is the same as +:any:`inverse` but with a different algorithm (using a loop and indexed +access). + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse_ref() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("inverse_arr", + &Perm16::inverse_arr, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. This function is the same as +:any:`inverse` but with a different algorithm (using reference cast to arrays). + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse_arr() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("inverse_sort", + &Perm16::inverse_sort, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. This function is the same as +:any:`inverse` but with a different algorithm: insert the identity in the least +significant bits and sort using a sorting network. + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse_sort() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("inverse_find", + &Perm16::inverse_find, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. This function is the same as +:any:`inverse` but with a different algorithm (some kind of vectorized +dichotomic search). + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse_find() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("inverse_pow", + &Perm16::inverse_pow, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. This function is the same as +:any:`inverse` but with a different algorithm (raising to the power +:math:`\text{lcm}(1, 2, ..., n) - 1`). + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse_pow() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("inverse_cycl", + &Perm16::inverse_cycl, + R"pbdoc( +:sig=(self: Perm16) -> Perm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. This function is the same as +:any:`inverse` but with a different algorithm (compute power from :math:`n/2` +to :math:`n`, when :math:`\sigma^k(i)=i` then +:math:`\sigma^{-1}(i)=\sigma^{k-1}(i)`). + +:returns: The inverse of *self*. +:rtype: Perm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([0,3,2,4,1]) + >>> x.inverse_pow() + Perm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def( + "lehmer", + [](Perm16 const& self) { return Vect16(self.lehmer()); }, + R"pbdoc( +:sig=(self: Perm16) -> Vect16: + +Returns the Lehmer code of a permutation. + +This function returns the Lehmer code of a permutation computed using fast +vector comparison. + +:returns: the Lehmer code. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).lehmer() + Vect16([ 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +)pbdoc"); + + thing.def( + "lehmer_ref", + [](Perm16 const& self) { return Vect16(self.lehmer_ref()); }, + R"pbdoc( +:sig=(self: Perm16) -> Vect16: + +Returns the Lehmer code of a permutation. + +This function returns the Lehmer code of a permutation computed +using loop and indexed access. + +:returns: the Lehmer code. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).lehmer_ref() + Vect16([ 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +)pbdoc"); + + thing.def( + "lehmer_arr", + [](Perm16 const& self) { return Vect16(self.lehmer_arr()); }, + R"pbdoc( +:sig=(self: Perm16) -> Vect16: + +Returns the Lehmer code of a permutation. + +This function returns the Lehmer code of a permutation computed +using array, loop, and indexed access. + +:returns: the Lehmer code. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).lehmer_arr() + Vect16([ 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +)pbdoc"); + + thing.def("length", + &Perm16::length, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the Coxeter length of a permutation. + +This function returns the Coxeter length (i.e. number of inversions) of the +permutation *self* (using vector Lehmer and fast horizontal sum). + +:returns: The Coxeter length. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).length() + 4 +)pbdoc"); + + thing.def("length_ref", + &Perm16::length_ref, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the Coxeter length of a permutation. + +This function returns the Coxeter length (i.e. number of inversions) of the +permutation *self* (using loop and indexed access). + +:returns: The Coxeter length. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).length_ref() + 4 +)pbdoc"); + + thing.def("length_arr", + &Perm16::length_arr, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the Coxeter length of a permutation. + +This function returns the Coxeter length (i.e. number of inversions) of the +permutation *self* (using loop and indexed access after a cast to +``std::array``). + +:returns: The Coxeter length. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).length_arr() + 4 +)pbdoc"); + + thing.def("nb_descents", + &Perm16::nb_descents, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the number of descent of a permutation. + +This function returns the the number of descent of a permutation. +(found using vector shift and comparison). + +:returns: The number of descents in *self*. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).nb_descents() + 2 +)pbdoc"); + + thing.def("nb_descents_ref", + &Perm16::nb_descents_ref, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the number of descent of a permutation. + +This function returns the the number of descent of a permutation. +(found using a loop). + +:returns: The number of descents in *self*. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> Perm16([0,3,2,4,1]).nb_descents_ref() + 2 +)pbdoc"); + + thing.def( + "cycles_partition", + [](Perm16 const& self) { return Vect16(self.cycles_partition()); }, + R"pbdoc( +:sig=(self: Perm16) -> Vect16: + +Returns the set partition of the cycles of a permutation. + +This function returns the set partition of the cycles of a permutation, +represented the vector :math:`v` where :math:`v[i]` contains the smallest +element in the cycle of :math:`v` in *self*. + +:returns: The set partition of the cycles of the permutation. +:rtype: Vect16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([1,2,3,6,0,5,4,7,8,9,10,11,12,15,14,13]) + >>> x.cycles_partition() + Vect16([ 0, 0, 0, 0, 0, 5, 0, 7, 8, 9,10,11,12,13,14,13]) +)pbdoc"); + + thing.def("nb_cycles", + &Perm16::nb_cycles, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the number of cycles of a permutation. + +This function returns the number of cycles of a permutation. + +:returns: The number of cycles. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([1,2,3,6,0,5,4]) + >>> x.nb_cycles() + 11 +)pbdoc"); + + thing.def("nb_cycles_ref", + &Perm16::nb_cycles_ref, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the number of cycles of a permutation. + +This function returns the number of cycles of a permutation (different +algorithm using a boolean vector). + +:returns: The number of cycles. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([1,2,3,6,0,5,4]) + >>> x.nb_cycles_ref() + 11 +)pbdoc"); + + thing.def("nb_cycles_unroll", + &Perm16::nb_cycles_unroll, + R"pbdoc( +:sig=(self: Perm16) -> int: + +Returns the number of cycles of a permutation. + +This function returns the number of cycles of a permutation (different +algorithm using :any:`cycles_partition`). + +:returns: The number of cycles. +:rtype: int + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x = Perm16([1,2,3,6,0,5,4]) + >>> x.nb_cycles_unroll() + 11 +)pbdoc"); + + thing.def("left_weak_leq", + &Perm16::left_weak_leq, + py::arg("other"), + R"pbdoc( +:sig=(self: Perm16, other: Perm16) -> bool: + +Compare two permutations using the left weak order. + +This function returns ``True`` if *self* is less than *other* in +the left weak order. This function uses :any:`length`. + +:param other: the permutation for comparison. +:type other: Perm16 + +:returns: Whether *self* is less than or equal to *other*. +:rtype: bool + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x, y = Perm16([2, 0, 3, 1]), Perm16([3, 0, 2, 1]) + >>> x.left_weak_leq(y) + True +)pbdoc"); + + thing.def("left_weak_leq_ref", + &Perm16::left_weak_leq_ref, + py::arg("other"), + R"pbdoc( +:sig=(self: Perm16, other: Perm16) -> bool: + +Compare two permutations using the left weak order. + +This function returns ``True`` if *self* is less than *other* in the left weak +order. This function implements an algorithm testing inclusion of inversions +one by one. + +:param other: the permutation for comparison. +:type other: Perm16 + +:returns: Whether *self* is less than or equal to *other*. +:rtype: bool + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x, y = Perm16([2, 0, 3, 1]), Perm16([3, 0, 2, 1]) + >>> x.left_weak_leq_ref(y) + True +)pbdoc"); + + thing.def("left_weak_leq_length", + &Perm16::left_weak_leq_length, + py::arg("other"), + R"pbdoc( +:sig=(self: Perm16, other: Perm16) -> bool: + +Compare two permutations using the left weak order. + +This function returns ``True`` if *self* is less than *other* in the left weak +order. This function implements an algorithm with vectorized test of inclusion. + +:param other: the permutation for comparison. +:type other: Perm16 + +:returns: Whether *self* is less than or equal to *other*. +:rtype: bool + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import Perm16 + >>> x, y = Perm16([2, 0, 3, 1]), Perm16([3, 0, 2, 1]) + >>> x.left_weak_leq_length(y) + True +)pbdoc"); + } // init_hpcombi_perm16 + + //////////////////////////////////////////////////////////////////////// + // PPerm16 + //////////////////////////////////////////////////////////////////////// + + void init_hpcombi_pperm16(py::module& m) { + py::class_ thing(m, + "hpcombi_PPerm16", + R"pbdoc( +Class representing partial permutations. + +SIMD accelerated class :any:`PPerm16` representing partial permutations on +up to ``16`` points (i.e. bijections between subsets of :math:`\{0\dots 15\}`). +Undefined images are encoded as ``255``. + +This class belongs to the ``hpcombi`` subpackage of ``libsemigroups_pybind11``. + +The functionality described on this page is only available if +:any:`LIBSEMIGROUPS_HPCOMBI_ENABLED` is ``True``. + +:any:`Perm16` inherits from :any:`PTransf16`. +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Special methods + //////////////////////////////////////////////////////////////////////// + + thing.def("__copy__", [](PPerm16 const& self) { return PPerm16(self); }); + + thing.def("__repr__", + [](PPerm16 const& self) { return repr(self, "PPerm16"); }); + + thing.def("__pow__", [](PPerm16 const& self, int n) { + if (n == -1) { + return self.inverse_ref(); + } + // TODO implement other powers + throw pybind11::type_error("unsupported operand type(s) ** or pow(): " + "'PPerm16' and 'int"); + }); + + //////////////////////////////////////////////////////////////////////// + // Static methods + //////////////////////////////////////////////////////////////////////// + + thing.def_static("one", + &PPerm16::one, + R"pbdoc( +:sig=one() -> PPerm16: + +Returns the identity permutation. + +This function returns the identity :any:`PPerm16` which fixes every value in +:math:`[0, 16)`. + +:returns: The identity permutation. +:rtype: PPerm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> x = PPerm16([1, 2, 0]) + >>> x * x.one() == PPerm16.one() * x == x + True +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////// + + thing.def(py::init<>(), R"pbdoc( +:sig=(self: PPerm16) -> None: + +Default constructor. + +Constructs a :any:`PPerm16` object with its entries uninitialized. This means +there is no guarantee about the values in the constructed object. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> x = PPerm16() +)pbdoc"); + + thing.def( // Seems like the last arg does nothing + py::init([](std::vector dom, std::vector ran) { + return ::libsemigroups::make(dom, ran, 16); + }), + R"pbdoc( +:sig=(self: PPerm16, dom: list[int], im: list[int]) -> None: + +Construct from domain and image. + +Constructs a partial permutation of degree *n* such that ``(dom[i])f = +im[i]`` for all ``i`` and which is undefined (``255`` represents undefined in +this context) on every other value in the range :math:`[0, n)`. + +:param dom: the domain. +:type dom: list[int] +:param im: the image. +:type im: list[int] + +:raises LibsemigroupsError: *dom* and *im* do not have the same size. +:raises LibsemigroupsError: any value in *dom* or *im* is greater than *15*. +:raises LibsemigroupsError: there are repeated entries in *dom* or *im*. +:raises TypeError: any value in *dom* or *im* is greater than *15*. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> PPerm16([1, 2], [3, 4]) + PPerm16([255, 3, 4,255,255,255,255,255,255,255,255,255,255,255,255,255]) +)pbdoc"); + + thing.def(py::init([](std::vector img) { + return ::libsemigroups::make(std::move(img)); + }), + R"pbdoc( +:sig=(self: PPerm16, img: list[int]) -> None: + +Construct a :any:`PPerm16` from a list of images. + +This function constructs a :any:`PPerm16` from the list *img* of its entries. +If the length of *img* is less than ``16``, then the constructed :any:`PPerm16` +is padded with fixed points at the end. + +:param img: The list of images. +:type img: list[int] + +:raises LibsemigroupsError: + if any value in *img* exceeds ``16`` and is not equal to ``255``. +:raises LibsemigroupsError: if the length of *img* exceeds ``16``. +:raises TypeError: if any value in *img* is larger than ``255``. + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> PPerm16([1, 255, 4, 10]) + PPerm16([ 1,255, 4,10, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + //////////////////////////////////////////////////////////////////////// + // Other methods + //////////////////////////////////////////////////////////////////////// + + thing.def( + "copy", + [](PPerm16 const& v) { return PPerm16(v); }, + R"pbdoc( +:sig=(self: PPerm16) -> PPerm16: + +Copy a :any:`PPerm16`. + +:returns: A copy of the argument. +:rtype: PPerm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> x = PPerm16([1, 2, 3, 4, 0]) + >>> x.copy() is not x + True + >>> x.copy() == x + True +)pbdoc"); + + thing.def("validate", + &PPerm16::validate, + py::arg("bound") = 16, + R"pbdoc( +:sig=(self: PPerm16, bound: int = 16) -> bool: + +Check whether or not a :any:`PPerm16` is well-defined. + +This function returns ``True`` if *self* is a well-defined partial permutation +(i.e. no defined image value is larger than ``15`` and no repeated image value) +on the values ``0`` up to *bound*. + +:param bound: the bound (defaults to ``16``). +:type bound: int + +:returns: Whether or not *self* is valid. +:rtype: bool + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> PPerm16([1, 0, 2]).validate() + True + >>> x = PPerm16([1, 0, 2]) + >>> x[0] = 0 + >>> x.validate() + False +)pbdoc"); + + thing.def("inverse_ref", + &PPerm16::inverse_ref, + R"pbdoc( +:sig=(self: PPerm16) -> PPerm16: + +Returns the inverse permutation. + +This function returns the inverse of *self*. The inverse of a partial perm +:math:`x` is the unique partial permutation :math:`y` such that :math:`xyx = x` +and :math:`xyx = x`. + +:returns: The inverse of *self*. +:rtype: PPerm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> x = PPerm16([0,3,2,4,1]) + >>> x.inverse_ref() + PPerm16([ 0, 4, 2, 1, 3, 5, 6, 7, 8, 9,10,11,12,13,14,15]) +)pbdoc"); + + thing.def("right_one", + // This is not a typo, everything is reversed in HPCombi + &PPerm16::left_one, + R"pbdoc( +:sig=(self: PPerm16) -> PPerm16: +Returns the right one of a partial transformation. + +This function returns a newly constructed :any:`PPerm16` with the same image as +*self* and that acts as the identity on *self* by right multiplication. + +:returns: The right one of *self*. +:rtype: PPerm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> x = PPerm16([1, 3, 4, 255, 10]) + >>> x * x.right_one() == x + True +)pbdoc"); + + thing.def("left_one", + // This is not a typo, everything is reversed in HPCombi + &PPerm16::right_one, + R"pbdoc( +:sig=(self: PPerm16) -> PPerm16: + +Returns the left one of a partial transformation. + +This function returns a newly constructed :any:`PPerm16` with the same image as +*self* and that acts as the identity on *self* by left multiplication. + +:returns: The left one of *self*. +:rtype: PPerm16 + +.. doctest:: + + >>> from libsemigroups_pybind11.hpcombi import PPerm16 + >>> x = PPerm16([1, 3, 4, 255, 10]) + >>> x.left_one() * x == x + True +)pbdoc"); + } // init_hpcombi_pperm16 + } // namespace +} // namespace HPCombi + +namespace libsemigroups { + void init_hpcombi(py::module& m) { + HPCombi::init_hpcombi_vect16(m); + HPCombi::init_hpcombi_ptransf16(m); + HPCombi::init_hpcombi_transf16(m); + HPCombi::init_hpcombi_perm16(m); + HPCombi::init_hpcombi_pperm16(m); + } +} // namespace libsemigroups +#endif diff --git a/src/konieczny.cpp b/src/konieczny.cpp index f962e476..d8778813 100644 --- a/src/konieczny.cpp +++ b/src/konieczny.cpp @@ -751,6 +751,12 @@ not already known. bind_konieczny>(m, "PPerm1"); bind_konieczny>(m, "PPerm2"); bind_konieczny>(m, "PPerm4"); + +#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + bind_konieczny(m, "HPCombiPTransf16"); + bind_konieczny(m, "HPCombiTransf16"); + bind_konieczny(m, "HPCombiPPerm16"); +#endif } } // namespace libsemigroups diff --git a/src/libsemigroups_pybind11/__init__.py b/src/libsemigroups_pybind11/__init__.py index c76b46b5..25bd333f 100644 --- a/src/libsemigroups_pybind11/__init__.py +++ b/src/libsemigroups_pybind11/__init__.py @@ -13,6 +13,7 @@ import libsemigroups_pybind11.congruence import libsemigroups_pybind11.forest import libsemigroups_pybind11.froidure_pin +import libsemigroups_pybind11.hpcombi import libsemigroups_pybind11.kambites import libsemigroups_pybind11.knuth_bendix import libsemigroups_pybind11.matrix @@ -52,6 +53,8 @@ try: from _libsemigroups_pybind11 import ( + LIBSEMIGROUPS_EIGEN_ENABLED, + LIBSEMIGROUPS_HPCOMBI_ENABLED, LIMIT_MAX, NEGATIVE_INFINITY, PBR, diff --git a/src/libsemigroups_pybind11/froidure_pin.py b/src/libsemigroups_pybind11/froidure_pin.py index ff4e1a0b..de6e4db1 100644 --- a/src/libsemigroups_pybind11/froidure_pin.py +++ b/src/libsemigroups_pybind11/froidure_pin.py @@ -18,6 +18,7 @@ from typing_extensions import Self as _Self from _libsemigroups_pybind11 import ( + LIBSEMIGROUPS_HPCOMBI_ENABLED as _LIBSEMIGROUPS_HPCOMBI_ENABLED, PBR as _PBR, Bipartition as _Bipartition, BMat as _BMat, @@ -94,6 +95,19 @@ ) from .detail.decorators import copydoc as _copydoc +if _LIBSEMIGROUPS_HPCOMBI_ENABLED: + from _libsemigroups_pybind11 import ( + FroidurePinHPCombiPerm16 as _FroidurePinHPCombiPerm16, + FroidurePinHPCombiPPerm16 as _FroidurePinHPCombiPPerm16, + FroidurePinHPCombiPTransf16 as _FroidurePinHPCombiPTransf16, + FroidurePinHPCombiTransf16 as _FroidurePinHPCombiTransf16, + hpcombi_Perm16 as _HPCombiPerm16, + hpcombi_PPerm16 as _HPCombiPPerm16, + hpcombi_PTransf16 as _HPCombiPTransf16, + hpcombi_Transf16 as _HPCombiTransf16, + ) + + ######################################################################## # The FroidurePin python class ######################################################################## @@ -104,32 +118,42 @@ class FroidurePin(_CxxWrapper): __doc__ = _FroidurePinPBR.__doc__ - _py_template_params_to_cxx_type = { - (_BMat,): _FroidurePinBMat, - (_BMat8,): _FroidurePinBMat8, - (_Bipartition,): _FroidurePinBipartition, - (_IntMat,): _FroidurePinIntMat, - (_MaxPlusMat,): _FroidurePinMaxPlusMat, - (_MaxPlusTruncMat,): _FroidurePinMaxPlusTruncMat, - (_MinPlusMat,): _FroidurePinMinPlusMat, - (_MinPlusTruncMat,): _FroidurePinMinPlusTruncMat, - (_NTPMat,): _FroidurePinNTPMat, - (_PBR,): _FroidurePinPBR, - (_PPerm1,): _FroidurePinPPerm1, - (_PPerm2,): _FroidurePinPPerm2, - (_PPerm4,): _FroidurePinPPerm4, - (_Perm1,): _FroidurePinPerm1, - (_Perm2,): _FroidurePinPerm2, - (_Perm4,): _FroidurePinPerm4, - (_ProjMaxPlusMat,): _FroidurePinProjMaxPlusMat, - (_Transf1,): _FroidurePinTransf1, - (_Transf2,): _FroidurePinTransf2, - (_Transf4,): _FroidurePinTransf4, - (_KBEStringTrie,): _FroidurePinKBEStringRewriteTrie, - (_KBEStringFromLeft,): _FroidurePinKBEStringRewriteFromLeft, - (_KBEWordTrie,): _FroidurePinKBEWordRewriteTrie, - (_KBEWordFromLeft,): _FroidurePinKBEWordRewriteFromLeft, - } + _py_template_params_to_cxx_type = ( + { + (_BMat,): _FroidurePinBMat, + (_BMat8,): _FroidurePinBMat8, + (_Bipartition,): _FroidurePinBipartition, + (_IntMat,): _FroidurePinIntMat, + (_MaxPlusMat,): _FroidurePinMaxPlusMat, + (_MaxPlusTruncMat,): _FroidurePinMaxPlusTruncMat, + (_MinPlusMat,): _FroidurePinMinPlusMat, + (_MinPlusTruncMat,): _FroidurePinMinPlusTruncMat, + (_NTPMat,): _FroidurePinNTPMat, + (_PBR,): _FroidurePinPBR, + (_PPerm1,): _FroidurePinPPerm1, + (_PPerm2,): _FroidurePinPPerm2, + (_PPerm4,): _FroidurePinPPerm4, + (_Perm1,): _FroidurePinPerm1, + (_Perm2,): _FroidurePinPerm2, + (_Perm4,): _FroidurePinPerm4, + (_ProjMaxPlusMat,): _FroidurePinProjMaxPlusMat, + (_Transf1,): _FroidurePinTransf1, + (_Transf2,): _FroidurePinTransf2, + (_Transf4,): _FroidurePinTransf4, + (_KBEStringTrie,): _FroidurePinKBEStringRewriteTrie, + (_KBEStringFromLeft,): _FroidurePinKBEStringRewriteFromLeft, + (_KBEWordTrie,): _FroidurePinKBEWordRewriteTrie, + (_KBEWordFromLeft,): _FroidurePinKBEWordRewriteFromLeft, + } + | { + (_HPCombiPTransf16,): _FroidurePinHPCombiPTransf16, + (_HPCombiTransf16,): _FroidurePinHPCombiTransf16, + (_HPCombiPerm16,): _FroidurePinHPCombiPerm16, + (_HPCombiPPerm16,): _FroidurePinHPCombiPPerm16, + } + if _LIBSEMIGROUPS_HPCOMBI_ENABLED + else {} + ) _cxx_type_to_py_template_params = dict( zip( diff --git a/src/libsemigroups_pybind11/hpcombi.py b/src/libsemigroups_pybind11/hpcombi.py new file mode 100644 index 00000000..990d18b5 --- /dev/null +++ b/src/libsemigroups_pybind11/hpcombi.py @@ -0,0 +1,34 @@ +# Copyright (c) 2025 J. D. Mitchell +# +# Distributed under the terms of the GPL license version 3. +# +# The full license is in the file LICENSE, distributed with this software. + +"""This page contains the documentation for the HPCombi functionality exposed +in ``libsemigroups_pybind11``. +""" + +from _libsemigroups_pybind11 import LIBSEMIGROUPS_HPCOMBI_ENABLED + +if LIBSEMIGROUPS_HPCOMBI_ENABLED: + from _libsemigroups_pybind11 import ( # pylint: disable=no-name-in-module + hpcombi_Perm16 as Perm16, + hpcombi_PPerm16 as PPerm16, + hpcombi_PTransf16 as PTransf16, + hpcombi_Transf16 as Transf16, + hpcombi_Vect16 as Vect16, + ) + + # The following fools sphinx into thinking that Vect16 etc are not + # aliases. + + Perm16.__module__ = __name__ + Perm16.__name__ = "Perm16" + PPerm16.__module__ = __name__ + PPerm16.__name__ = "PPerm16" + PTransf16.__module__ = __name__ + PTransf16.__name__ = "PTransf16" + Transf16.__module__ = __name__ + Transf16.__name__ = "Transf16" + Vect16.__module__ = __name__ + Vect16.__name__ = "Vect16" diff --git a/src/libsemigroups_pybind11/konieczny.py b/src/libsemigroups_pybind11/konieczny.py index 6b7ec494..57bcd974 100644 --- a/src/libsemigroups_pybind11/konieczny.py +++ b/src/libsemigroups_pybind11/konieczny.py @@ -14,6 +14,7 @@ from typing_extensions import Self as _Self from _libsemigroups_pybind11 import ( + LIBSEMIGROUPS_HPCOMBI_ENABLED as _LIBSEMIGROUPS_HPCOMBI_ENABLED, BMat as _BMat, BMat8 as _BMat8, KoniecznyBMat as _KoniecznyBMat, @@ -49,6 +50,19 @@ ) from .detail.decorators import copydoc as _copydoc +if _LIBSEMIGROUPS_HPCOMBI_ENABLED: + from _libsemigroups_pybind11 import ( + KoniecznyHPCombiPPerm16 as _KoniecznyHPCombiPPerm16, + KoniecznyHPCombiPPerm16DClass as _KoniecznyHPCombiPPerm16DClass, + KoniecznyHPCombiPTransf16 as _KoniecznyHPCombiPTransf16, + KoniecznyHPCombiPTransf16DClass as _KoniecznyHPCombiPTransf16DClass, + KoniecznyHPCombiTransf16 as _KoniecznyHPCombiTransf16, + KoniecznyHPCombiTransf16DClass as _KoniecznyHPCombiTransf16DClass, + hpcombi_PPerm16 as _HPCombiPPerm16, + hpcombi_PTransf16 as _HPCombiPTransf16, + hpcombi_Transf16 as _HPCombiTransf16, + ) + ######################################################################## # Konieczny python class ######################################################################## @@ -58,16 +72,25 @@ class Konieczny(_CxxWrapper): Element = _TypeVar("Element") __doc__ = _KoniecznyBMat.__doc__ - _py_template_params_to_cxx_type = { - (_BMat,): _KoniecznyBMat, - (_BMat8,): _KoniecznyBMat8, - (_PPerm1,): _KoniecznyPPerm1, - (_PPerm2,): _KoniecznyPPerm2, - (_PPerm4,): _KoniecznyPPerm4, - (_Transf1,): _KoniecznyTransf1, - (_Transf2,): _KoniecznyTransf2, - (_Transf4,): _KoniecznyTransf4, - } + _py_template_params_to_cxx_type = ( + { + (_BMat,): _KoniecznyBMat, + (_BMat8,): _KoniecznyBMat8, + (_PPerm1,): _KoniecznyPPerm1, + (_PPerm2,): _KoniecznyPPerm2, + (_PPerm4,): _KoniecznyPPerm4, + (_Transf1,): _KoniecznyTransf1, + (_Transf2,): _KoniecznyTransf2, + (_Transf4,): _KoniecznyTransf4, + } + | { + (_HPCombiPPerm16,): _KoniecznyHPCombiPPerm16, + (_HPCombiPTransf16,): _KoniecznyHPCombiPTransf16, + (_HPCombiTransf16,): _KoniecznyHPCombiTransf16, + } + if _LIBSEMIGROUPS_HPCOMBI_ENABLED + else {} + ) _cxx_type_to_py_template_params = dict( zip( @@ -88,16 +111,25 @@ class DClass(_CxxWrapper): Element = _TypeVar("Element") - _py_template_params_to_cxx_type = { - (_BMat,): _KoniecznyBMatDClass, - (_BMat8,): _KoniecznyBMat8DClass, - (_PPerm1,): _KoniecznyPPerm1DClass, - (_PPerm2,): _KoniecznyPPerm2DClass, - (_PPerm4,): _KoniecznyPPerm4DClass, - (_Transf1,): _KoniecznyTransf1DClass, - (_Transf2,): _KoniecznyTransf2DClass, - (_Transf4,): _KoniecznyTransf4DClass, - } + _py_template_params_to_cxx_type = ( + { + (_BMat,): _KoniecznyBMatDClass, + (_BMat8,): _KoniecznyBMat8DClass, + (_PPerm1,): _KoniecznyPPerm1DClass, + (_PPerm2,): _KoniecznyPPerm2DClass, + (_PPerm4,): _KoniecznyPPerm4DClass, + (_Transf1,): _KoniecznyTransf1DClass, + (_Transf2,): _KoniecznyTransf2DClass, + (_Transf4,): _KoniecznyTransf4DClass, + } + | { + (_HPCombiPPerm16,): _KoniecznyHPCombiPPerm16DClass, + (_HPCombiPTransf16,): _KoniecznyHPCombiPTransf16DClass, + (_HPCombiTransf16,): _KoniecznyHPCombiTransf16DClass, + } + if _LIBSEMIGROUPS_HPCOMBI_ENABLED + else {} + ) _cxx_type_to_py_template_params = dict( zip( diff --git a/src/libsemigroups_pybind11/schreier_sims.py b/src/libsemigroups_pybind11/schreier_sims.py index f800bfb5..03346ba4 100644 --- a/src/libsemigroups_pybind11/schreier_sims.py +++ b/src/libsemigroups_pybind11/schreier_sims.py @@ -13,6 +13,7 @@ from typing_extensions import Self as _Self from _libsemigroups_pybind11 import ( + LIBSEMIGROUPS_HPCOMBI_ENABLED as _LIBSEMIGROUPS_HPCOMBI_ENABLED, Perm1 as _Perm1, Perm2 as _Perm2, # Perm4 as _Perm4, @@ -30,6 +31,12 @@ ) from .detail.decorators import copydoc as _copydoc +if _LIBSEMIGROUPS_HPCOMBI_ENABLED: + from _libsemigroups_pybind11 import ( + SchreierSimsHPCombiPerm16 as _SchreierSimsHPCombiPerm16, + hpcombi_Perm16 as _HPCombiPerm16, + ) + Element = _TypeVar("Element") ######################################################################## @@ -40,11 +47,16 @@ class SchreierSims(_CxxWrapper): __doc__ = _SchreierSimsPerm1.__doc__ - _py_template_params_to_cxx_type = { - (_Perm1,): _SchreierSimsPerm1, - (_Perm2,): _SchreierSimsPerm2, - # (_Perm4,): _SchreierSims, - } + _py_template_params_to_cxx_type = ( + { + (_Perm1,): _SchreierSimsPerm1, + (_Perm2,): _SchreierSimsPerm2, + # (_Perm4,): _SchreierSims, + } + | {(_HPCombiPerm16,): _SchreierSimsHPCombiPerm16} + if _LIBSEMIGROUPS_HPCOMBI_ENABLED + else {} + ) _cxx_type_to_py_template_params = dict( zip( diff --git a/src/main.cpp b/src/main.cpp index 1681ff16..caba008a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,13 +36,13 @@ namespace libsemigroups { #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; #else - m.attr("__version__") = "dev"; + m.attr("__version__") = "dev"; #endif #ifdef LIBSEMIGROUPS_EIGEN_ENABLED m.attr("LIBSEMIGROUPS_EIGEN_ENABLED") = static_cast(LIBSEMIGROUPS_EIGEN_ENABLED); #else - m.attr("LIBSEMIGROUPS_EIGEN_ENABLED") = false; + m.attr("LIBSEMIGROUPS_EIGEN_ENABLED") = false; #endif #ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED @@ -78,6 +78,7 @@ namespace libsemigroups { // Must be before cong classes init_present(m); init_inverse_present(m); + // Must be before anything with visualisation init_dot(m); @@ -93,6 +94,10 @@ namespace libsemigroups { init_pbr(m); init_transf(m); +#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + init_hpcombi(m); +#endif + // Must come before paths init_words(m); diff --git a/src/main.hpp b/src/main.hpp index 93968043..436c73af 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -24,6 +24,7 @@ #include +#include // for LIBSEMIGROUPS_HPCOMBI_ENABLED #include namespace libsemigroups { @@ -81,6 +82,10 @@ namespace libsemigroups { void init_word_graph(py::module&); void init_words(py::module&); +#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + void init_hpcombi(py::module&); +#endif + template using int_or_unsigned_constant = std::variant; diff --git a/src/schreier-sims.cpp b/src/schreier-sims.cpp index 272dc7fb..2cc5b5ed 100644 --- a/src/schreier-sims.cpp +++ b/src/schreier-sims.cpp @@ -23,8 +23,9 @@ #include // for allocator, make_unique, unique_ptr // libsemigroups headers -#include +#include #include +#include // pybind11.... #include @@ -112,6 +113,7 @@ Add a base point to the stabiliser chain. :raises LibsemigroupsError: if :any:`finished()` returns ``True``. :complexity: Linear in the current number of base points.)pbdoc"); + thing.def("add_generator", &SchreierSims_::add_generator, py::arg("x"), @@ -514,12 +516,15 @@ corresponding to the intersection of *x* and *y*. :raises LibsemigroupsError: if *result* is not empty. )pbdoc"); } // bind_schreier_sims - } // namespace + } // namespace void init_schreier_sims(py::module& m) { // One call to bind is required per list of types bind_schreier_sims<255, uint8_t, Perm<0, uint8_t>>(m, "Perm1"); bind_schreier_sims<511, uint16_t, Perm<0, uint16_t>>(m, "Perm2"); +#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED + bind_schreier_sims<16, uint8_t, HPCombi::Perm16>(m, "HPCombiPerm16"); +#endif } } // namespace libsemigroups diff --git a/tests/test_hpcombi.py b/tests/test_hpcombi.py new file mode 100644 index 00000000..0c2c488b --- /dev/null +++ b/tests/test_hpcombi.py @@ -0,0 +1,563 @@ +# Copyright (c) 2025, James D. Mitchell +# +# Distributed under the terms of the GPL license version 3. +# +# The full license is in the file LICENSE, distributed with this software. + +# pylint: disable=missing-function-docstring + +"""This module contains some tests for the libsemigroups_pybind11 functionality +arising from hpcombi.*pp in libsemigroups. +""" + +from copy import copy + +import pytest + +from libsemigroups_pybind11 import LIBSEMIGROUPS_HPCOMBI_ENABLED, LibsemigroupsError + +if LIBSEMIGROUPS_HPCOMBI_ENABLED: + from libsemigroups_pybind11.hpcombi import Perm16, PPerm16, PTransf16, Transf16, Vect16 + + ######################################################################## + # Vect16 + ######################################################################## + + def test_hpcombi_vect16_hash(): + d = {Vect16([0, 10]): 0} + assert d[Vect16([0, 10])] == 0 + + def test_hpcombi_vect16_copy(): + x = Vect16([1, 2, 0]) + assert copy(x) is not x + assert x.copy() is not x + assert x.copy() == copy(x) + assert isinstance(x.copy(), Vect16) + + def test_hpcombi_vect16_construct_from_list(): + x = Vect16([0, 10]) + assert x[0] == 0 + assert x[1] == 10 + for i in range(2, 16): + assert x[i] == 0 + with pytest.raises(LibsemigroupsError): + x = Vect16([0] * 17) + + def test_hpcombi_vect16_set_item(): + x = Vect16([0] * 16) + x[1] = 10 + assert x == Vect16([0, 10] + [0] * 14) + + def test_hpcombi_vect16_lt(): + x = Vect16([0]) + y = Vect16([1]) + assert x < y + assert not x > y # pylint: disable=unnecessary-negation + assert not y < x # pylint: disable=unnecessary-negation + assert y > x + + def test_hpcombi_vect16_eq(): + x = Vect16([0]) + y = Vect16([1]) + assert x != y + assert y != x + assert x == Vect16([0]) + + def test_hpcombi_vect16_size(): + assert len(Vect16()) == 16 + + def test_hpcombi_vect16_first_diff(): + assert Vect16([1, 2, 3, 4, 255]).first_diff(Vect16([1, 2, 3, 4, 245])) == 4 + assert Vect16([1, 2, 3, 4, 255]).first_diff(Vect16([1, 2, 3, 4, 245]), 4) == 16 + assert Vect16().first_diff(Vect16()) == 16 + assert Vect16().first_diff(Vect16(), 12) == 16 + assert Vect16().first_diff(Vect16(), 100) == 16 + assert Vect16().first_diff(Vect16(), 256) == 16 + + def test_hpcombi_vect16_last_diff(): + assert Vect16([1, 2, 3, 4, 255]).last_diff(Vect16([1, 2, 3, 4, 245])) == 4 + assert Vect16().last_diff(Vect16()) == 16 + + def test_hpcombi_vect16_first_zero(): + assert Vect16([1, 2, 3, 4, 255]).first_zero() == 5 + assert Vect16().first_zero() == 0 + assert Vect16([1, 2, 3, 4, 255]).first_zero(3) == 16 + + def test_hpcombi_vect16_last_zero(): + assert Vect16([1, 2, 3, 4, 255]).last_zero() == 15 + assert Vect16([1, 2, 3, 4, 255]).last_zero(3) == 16 + assert Vect16().last_zero() == 15 + + def test_hpcombi_vect16_first_non_zero(): + assert Vect16([1, 2, 3, 4, 255]).first_non_zero() == 0 + assert Vect16([1, 2, 3, 4, 255]).first_non_zero(3) == 0 + assert Vect16().first_non_zero() == 16 + + def test_hpcombi_vect16_last_non_zero(): + assert Vect16([1, 2, 3, 4, 255]).last_non_zero() == 4 + assert Vect16([1, 2, 3, 4, 255]).last_non_zero(3) == 2 + assert Vect16().last_non_zero() == 16 + + def test_hpcombi_vect16_iterator(): + v = Vect16([1, 2, 3, 4, 255, 0, 10, 69, 0, 0, 66, 0, 0, 0, 43]) + assert list(v) == [1, 2, 3, 4, 255, 0, 10, 69, 0, 0, 66, 0, 0, 0, 43, 0] + + def test_hpcombi_vect16_less_partial(): + u = Vect16([0, 1, 2, 10]) + v = Vect16([0, 1, 2, 3]) + + assert v.less_partial(u, 3) == 0 + assert v.less_partial(u, 4) == -7 + assert u.less_partial(v, 4) == 7 + assert u.less_partial(v, 16) == 7 + assert v.less_partial(u, 16) == -7 + + def test_hpcombi_vect16_sum(): + assert Vect16([0, 1, 2, 3]).sum() == 6 + assert Vect16([0, 1, 2, 10]).sum() == 13 + assert Vect16([1, 2, 3, 255]).sum() == 5 + assert Vect16([0, 1, 2, 3]).sum() == 6 + + def test_hpcombi_vect16_partial_sums(): + assert Vect16([0, 1, 2, 3]).partial_sums() == Vect16([0, 1, 3] + [6] * 13) + assert Vect16([0, 1, 2, 255]).partial_sums() == Vect16([0, 1, 3] + [2] * 13) + + def test_hpcombi_vect16_eval16(): + assert Vect16([5, 5, 2, 5, 1, 6, 12, 4, 0, 3, 2, 11, 12, 13, 14, 15]).eval16() == Vect16( + [1, 1, 2, 1, 1, 3, 1, 0, 0, 0, 0, 1, 2, 1, 1, 1] + ) + + def test_hpcombi_vect16_is_permutation(): + assert not Vect16([5, 5, 2, 5, 1, 6, 12, 4, 0, 3, 2, 11, 12, 13, 14, 15]).is_permutation() + assert not Vect16([1, 0, 2, 3, 4, 4]).is_permutation() + assert Vect16([1, 0, 2, 3, 4] + list(range(5, 16))).is_permutation(4) + assert Vect16([1, 0, 2, 3, 4] + list(range(5, 16))).is_permutation(5) + assert Vect16([1, 0, 2, 3, 4] + list(range(5, 16))).is_permutation() + + ######################################################################## + # PTransf16 + ######################################################################## + + def test_hpcombi_ptransf16_hash(): + d = {PTransf16([0, 10]): 0} + assert d[PTransf16([0, 10])] == 0 + + def test_hpcombi_ptransf16_copy(): + x = PTransf16([1, 2, 0]) + assert copy(x) is not x + assert x.copy() is not x + assert x.copy() == copy(x) + assert isinstance(x.copy(), PTransf16) + + def test_hpcombi_ptransf16_construct_from_list(): + x = PTransf16([0, 10]) + assert x[0] == 0 + assert x[1] == 10 + for i in range(2, 16): + assert x[i] == i + + def test_hpcombi_ptransf16_construct_dom_range(): + x = PTransf16([0, 10], [10, 0]) + assert x == PTransf16( + [10, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255] + ) + + with pytest.raises(LibsemigroupsError): + x = PTransf16([0, 0], [10, 0]) + + with pytest.raises(LibsemigroupsError): + x = PTransf16([0, 17], [10, 0]) + + with pytest.raises(LibsemigroupsError): + x = PTransf16([0, 1], [10, 17]) + + with pytest.raises(LibsemigroupsError): + x = PTransf16([0, 1], [10]) + + with pytest.raises(LibsemigroupsError): + x = PTransf16(list(range(17)), list(range(17))) + + def test_hpcombi_ptransf16_set_item(): + x = PTransf16([0] * 16) + x[1] = 10 + assert x == PTransf16([0, 10] + [0] * 14) + + def test_hpcombi_ptransf16_lt(): + x = PTransf16() + y = PTransf16([1]) + assert x < y + assert not x > y # pylint: disable=unnecessary-negation + assert not y < x # pylint: disable=unnecessary-negation + assert y > x + + def test_hpcombi_ptransf16_mul(): + x = PTransf16([1, 0, 1]) + y = PTransf16([0, 0, 2]) + assert y * x == PTransf16([1, 1, 1]) + + def test_hpcombi_ptransf16_one(): + assert PTransf16.one() == PTransf16(list(range(16))) + x = PTransf16([1, 0, 1]) + assert x.one() == PTransf16.one() + assert isinstance(PTransf16.one(), PTransf16) + + def test_hpcombi_ptransf16_len(): + assert len(PTransf16()) == 16 + + def test_hpcombi_ptransf16_validate(): + assert PTransf16.one().validate() + assert PTransf16.one().validate(3) + assert PTransf16([0, 1, 255]).validate(3) + + def test_hpcombi_ptransf16_image_mask(): + x = PTransf16([1, 0, 1]) + assert x.image_mask_ref(True) == Vect16([0, 0, 255] + [0] * 13) + assert x.image_mask_ref(False) == Vect16([255, 255, 0] + [255] * 13) + assert x.image_mask(True) == Vect16([0, 0, 255] + [0] * 13) + assert x.image_mask(False) == Vect16([255, 255, 0] + [255] * 13) + + def test_hpcombi_ptransf16_image_bitset(): + x = PTransf16([1, 3, 1, 255, 10]) + assert x.image_bitset() == 65514 + assert bin(x.image_bitset()) == "0b1111111111101010" + assert x.image_bitset() & 1 << 1 + assert x.image_bitset() & 1 << 3 + assert x.image_bitset() & 1 << 10 + + assert bin(x.image_bitset(True)) == "0b10101" + assert not x.image_bitset(True) & 1 << 1 + assert not x.image_bitset(True) & 1 << 3 + assert not x.image_bitset(True) & 1 << 10 + assert x.image_bitset(False) == x.image_bitset() + + def test_hpcombi_ptransf16_domain_mask(): + assert PTransf16([1, 0, 1]).domain_mask(True) == Vect16([0] * 16) + assert PTransf16([1, 0, 1]).domain_mask(False) == Vect16([255] * 16) + + def test_hpcombi_ptransf16_domain_bitset(): + x = PTransf16([1, 3, 1, 255, 10]) + assert x.domain_bitset() == 65527 + assert bin(x.domain_bitset()) == "0b1111111111110111" + assert x.domain_bitset() & 1 + assert x.domain_bitset() & 1 << 1 + assert x.domain_bitset() & 1 << 2 + assert x.domain_bitset() & 1 << 4 + + assert bin(x.domain_bitset(True)) == "0b1000" + assert not x.domain_bitset(True) & 1 + assert not x.domain_bitset(True) & 1 << 1 + assert not x.domain_bitset(True) & 1 << 2 + assert not x.domain_bitset(True) & 1 << 4 + + assert x.domain_bitset(False) == x.domain_bitset() + + def test_hpcombi_ptransf16_right_one(): + x = PTransf16([1, 3, 1, 255, 10]) + assert x.image_bitset() == x.right_one().image_bitset() + assert x * x.right_one() == x + + def test_hpcombi_ptransf16_left_one(): + x = PTransf16([1, 3, 1, 255, 10]) + assert x.domain_bitset() == x.left_one().domain_bitset() + assert x.left_one() * x == x + + def test_hpcombi_ptransf16_rank(): + x = PTransf16([1, 3, 1, 255, 10]) + assert x.rank_ref() == 13 + assert x.rank() == 13 + assert x.rank_cmpestrm() == 13 + + def test_hpcombi_ptransf16_fix_points_mask(): + x = PTransf16([1, 3, 1, 255, 10]) + assert x.fix_points_mask() == Vect16([0] * 5 + [255] * 11) + assert x.fix_points_mask(True) == Vect16([255] * 5 + [0] * 11) + assert x.one().fix_points_mask() == Vect16([255] * 16) + assert x.one().fix_points_mask(True) == Vect16() + + def test_hpcombi_ptransf16_fix_points_bitset(): + x = PTransf16([1, 3, 2, 255, 10]) + assert x.fix_points_bitset() == 65508 + assert x.fix_points_bitset(False) == 65508 + assert x.fix_points_bitset(True) == 27 + assert bin(x.fix_points_bitset()) == "0b1111111111100100" + assert bin(x.fix_points_bitset(True)) == "0b11011" + + def test_hpcombi_ptransf16_smallest_fix_point(): + assert PTransf16([1, 3, 2, 255, 10]).smallest_fix_point() == 2 + assert PTransf16([1, 3, 3, 255, 10]).smallest_fix_point() == 5 + + def test_hpcombi_ptransf16_smallest_moved_point(): + assert PTransf16([1, 3, 2, 255, 10]).smallest_moved_point() == 0 + assert PTransf16.one().smallest_moved_point() == 255 + + def test_hpcombi_ptransf16_largest_fix_point(): + assert PTransf16([1, 3, 2, 255, 10]).largest_fix_point() == 15 + assert PTransf16([1, 3, 3, 255, 10]).largest_fix_point() == 15 + assert PTransf16.one().largest_fix_point() == 15 + + def test_hpcombi_ptransf16_largest_moved_point(): + assert PTransf16([1, 3, 2, 255, 10]).largest_moved_point() == 4 + # TODO update if + # https://github.com/libsemigroups/HPCombi/issues/65 + # is ever resolved. + assert PTransf16([255] * 16).largest_moved_point() == 15 + + def test_hpcombi_ptransf_nb_fix_points(): + assert PTransf16([1, 3, 2, 255, 10]).nb_fix_points() == 12 + assert PTransf16.one().nb_fix_points() == 16 + + ######################################################################## + # Transf16 + ######################################################################## + + def test_hpcombi_transf16_hash(): + d = {Transf16([0, 10]): 0} + assert d[Transf16([0, 10])] == 0 + + def test_hpcombi_transf16_copy(): + x = Transf16([1, 2, 0]) + assert copy(x) is not x + assert x.copy() is not x + assert x.copy() == copy(x) + assert isinstance(x.copy(), Transf16) + + def test_hpcombi_transf16_construct_from_list(): + x = Transf16([0, 10]) + assert x[0] == 0 + assert x[1] == 10 + for i in range(2, 16): + assert x[i] == i + with pytest.raises(LibsemigroupsError): + x = Transf16([0] * 17) + with pytest.raises(LibsemigroupsError): + x = Transf16([255]) + with pytest.raises(TypeError): + x = Transf16([300]) + + def test_hpcombi_transf16_set_item(): + x = Transf16() + x[1] = 10 + assert x == Transf16([0, 10] + [0] * 14) + + def test_hpcombi_transf16_to_from_ints(): + x = Transf16([0, 15]) + assert int(x) == 17863200012815736704 + assert Transf16(int(x)) == x + + def test_hpcombi_transf16_lt(): + x = Transf16() + y = Transf16([1]) + assert x < y + assert not x > y # pylint: disable=unnecessary-negation + assert not y < x # pylint: disable=unnecessary-negation + assert y > x + + def test_hpcombi_transf16_mul(): + x = Transf16([1, 0, 1]) + y = Transf16([0, 0, 2]) + assert y * x == Transf16([1, 1, 1]) + + def test_hpcombi_transf16_one(): + assert Transf16.one() == Transf16(list(range(16))) + assert isinstance(Transf16.one(), Transf16) + + ######################################################################## + # Perm16 + ######################################################################## + + def test_hpcombi_perm_hash(): + d = {Perm16([1, 0]): 0} + assert d[Perm16([1, 0])] == 0 + + def test_hpcombi_perm16_copy(): + x = Perm16([1, 2, 0]) + assert copy(x) is not x + assert x.copy() is not x + assert x.copy() == copy(x) + assert isinstance(x.copy(), Perm16) + + def test_hpcombi_perm16_one(): + assert Perm16.one() == Perm16(list(range(16))) + x = Perm16([1, 2, 0]) + assert x.one() == Perm16.one() + assert isinstance(x.one(), Perm16) + + def test_hpcombi_perm16_inverse(): + x = Perm16([1, 2, 0]) + assert x * x.inverse() == x.one() == x.inverse() * x + assert x**-1 == x.inverse() + + def test_hpcombi_perm16_inverse_ref(): + x = Perm16([1, 2, 0]) + assert x * x.inverse_ref() == x.one() == x.inverse_ref() * x + assert x**-1 == x.inverse_ref() + + def test_hpcombi_perm16_inverse_arr(): + x = Perm16([1, 2, 0]) + assert x * x.inverse_arr() == x.one() == x.inverse_arr() * x + assert x**-1 == x.inverse_arr() + + def test_hpcombi_perm16_inverse_sort(): + x = Perm16([1, 2, 0]) + assert x * x.inverse_sort() == x.one() == x.inverse_sort() * x + assert x**-1 == x.inverse_sort() + + def test_hpcombi_perm16_inverse_find(): + x = Perm16([1, 2, 0]) + assert x * x.inverse_find() == x.one() == x.inverse_find() * x + assert x**-1 == x.inverse_find() + + def test_hpcombi_perm16_inverse_pow(): + x = Perm16([1, 2, 0]) + assert x * x.inverse_pow() == x.one() == x.inverse_pow() * x + assert x**-1 == x.inverse_pow() + + def test_hpcombi_perm16_inverse_cycl(): + x = Perm16([1, 2, 0]) + assert x * x.inverse_cycl() == x.one() == x.inverse_cycl() * x + assert x**-1 == x.inverse_cycl() + + def test_hpcombi_perm16_lehmer(): + assert Perm16([0, 3, 2, 4, 1]).lehmer() == Vect16([0, 2, 1, 1]) + + def test_hpcombi_perm16_lehmer_ref(): + assert Perm16([0, 3, 2, 4, 1]).lehmer_ref() == Vect16([0, 2, 1, 1]) + + def test_hpcombi_perm16_length(): + assert Perm16([0, 3, 2, 4, 1]).length() == 4 + + def test_hpcombi_perm16_length_ref(): + assert Perm16([0, 3, 2, 4, 1]).length_ref() == 4 + + def test_hpcombi_perm16_length_arr(): + assert Perm16([0, 3, 2, 4, 1]).length_arr() == 4 + + def test_hpcombi_perm16_elementary_transposition(): + with pytest.raises(LibsemigroupsError): + Perm16.elementary_transposition(16) + assert [Perm16.elementary_transposition(i) for i in range(15)] == [ + Perm16([1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 5, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 6, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 9, 8, 10, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 9, 11, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 10, 12, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 11, 13, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 12, 14, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 13, 15]), + Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 14]), + ] + + def test_hpcombi_perm16_nb_descents(): + assert Perm16([0, 3, 2, 4, 1]).nb_descents() == 2 + + def test_hpcombi_perm16_nb_descents_ref(): + assert Perm16([0, 3, 2, 4, 1]).nb_descents_ref() == 2 + + def test_hpcombi_perm16_cycles_partition(): + assert Perm16( + [1, 2, 3, 6, 0, 5, 4, 7, 8, 9, 10, 11, 12, 15, 14, 13] + ).cycles_partition() == Vect16([0, 0, 0, 0, 0, 5, 0, 7, 8, 9, 10, 11, 12, 13, 14, 13]) + + def test_hpcombi_perm16_nb_cycles(): + x = Perm16([1, 2, 3, 6, 0, 5, 4]) + assert x.nb_cycles() == 11 + + def test_hpcombi_perm16_nb_cycles_ref(): + x = Perm16([1, 2, 3, 6, 0, 5, 4]) + assert x.nb_cycles_ref() == 11 + + def test_hpcombi_perm16_nb_cycles_unroll(): + x = Perm16([1, 2, 3, 6, 0, 5, 4]) + assert x.nb_cycles_unroll() == 11 + + def test_hpcombi_perm16_left_weak_leq(): + x, y = Perm16([2, 0, 3, 1]), Perm16([3, 0, 2, 1]) + assert x.left_weak_leq(y) + assert y.left_weak_leq(y) + assert not y.left_weak_leq(x) + + def test_hpcombi_perm16_left_weak_leq_ref(): + x, y = Perm16([2, 0, 3, 1]), Perm16([3, 0, 2, 1]) + assert x.left_weak_leq_ref(y) + assert y.left_weak_leq_ref(y) + assert not y.left_weak_leq_ref(x) + + def test_hpcombi_perm16_left_weak_leq_length(): + x, y = Perm16([2, 0, 3, 1]), Perm16([3, 0, 2, 1]) + assert x.left_weak_leq_length(y) + assert y.left_weak_leq_length(y) + assert not y.left_weak_leq_length(x) + + def test_hpcombi_perm16_unrankSJT(): # pylint: disable=invalid-name + assert Perm16.unrankSJT(2) == Perm16([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 13, 14]) + + ######################################################################## + # PPerm16 + ######################################################################## + + def test_hpcombi_pperm16_hash(): + d = {PPerm16([1, 0]): 0} + assert d[PPerm16([1, 0])] == 0 + + def test_hpcombi_pperm16_copy(): + x = PPerm16([1, 2, 0]) + assert copy(x) is not x + assert x.copy() is not x + assert x.copy() == copy(x) + assert isinstance(x.copy(), PPerm16) + + def test_hpcombi_pperm16_one(): + assert PPerm16.one() == PPerm16(list(range(16))) + x = PPerm16([1, 2, 0]) + assert x.one() == PPerm16.one() + assert isinstance(x.one(), PPerm16) + + def test_hpcombi_pperm16_init_from_list(): + with pytest.raises(LibsemigroupsError): + assert PPerm16([255, 0, 255, 0]) + with pytest.raises(LibsemigroupsError): + assert PPerm16([255, 0, 255, 17]) + with pytest.raises(TypeError): + assert PPerm16([255, 0, 255, 300]) + x = PPerm16([255, 0, 255, 1]) + assert x == PPerm16([255, 0, 255, 1]) + + def test_hpcombi_pperm16_init_from_dom_img(): + with pytest.raises(LibsemigroupsError): + assert PPerm16([0, 0], [1, 2]) + with pytest.raises(LibsemigroupsError): + assert PPerm16([1, 2], [0, 0]) + with pytest.raises(LibsemigroupsError): + assert PPerm16([255, 0], [1, 2]) + with pytest.raises(LibsemigroupsError): + assert PPerm16([1, 0], [1, 255]) + with pytest.raises(TypeError): + assert PPerm16([1, 0], [1, 300]) + x = PPerm16([1, 0], [0, 1]) + assert x == PPerm16([1, 0], [0, 1]) + + def test_hpcombi_pperm16_mult(): + x = PPerm16([1, 2], [3, 4]) + assert x * x == PPerm16([255] * 16) + + def test_hpcombi_pperm16_right_one(): + x = PPerm16([1, 3, 255, 10]) + assert x * x.right_one() == x + + def test_hpcombi_pperm16_left_one(): + x = PPerm16([1, 3, 255, 10]) + assert x.left_one() * x == x + + def test_hpcombi_pperm16_inverse_ref(): + x = PPerm16([1, 2, 0]) + assert x * x.inverse_ref() * x == x + assert x.inverse_ref() * x * x.inverse_ref() == x.inverse_ref() + assert x * x.inverse_ref() == x.left_one() + assert x.inverse_ref() * x == x.right_one() + assert x**-1 == x.inverse_ref() diff --git a/tests/test_konieczny.py b/tests/test_konieczny.py index 7456440f..2be08c0b 100644 --- a/tests/test_konieczny.py +++ b/tests/test_konieczny.py @@ -13,7 +13,19 @@ import pytest -from libsemigroups_pybind11 import BMat8, Konieczny, Matrix, MatrixKind, PPerm, ReportGuard, Transf +from libsemigroups_pybind11 import ( + LIBSEMIGROUPS_HPCOMBI_ENABLED, + BMat8, + Konieczny, + Matrix, + MatrixKind, + PPerm, + ReportGuard, + Transf, +) + +if LIBSEMIGROUPS_HPCOMBI_ENABLED: + from libsemigroups_pybind11 import hpcombi def BMat(x): @@ -427,3 +439,15 @@ def test_konieczny_return_policy(): assert S.D_class_of_element(gens[0]) is S.D_class_of_element(gens[0]) assert S.generator(0) is S.generator(0) assert S.D_class_of_element(gens[0]).rep() is S.D_class_of_element(gens[0]).rep() + + +if LIBSEMIGROUPS_HPCOMBI_ENABLED: + + def test_konieczny_hpcombi_ptranf16(): + S = Konieczny( + hpcombi.PTransf16([1, 0, 2, 3, 4, 5, 6, 7, 8]), + hpcombi.PTransf16([7, 0, 1, 2, 3, 4, 5, 6, 8]), + hpcombi.PTransf16([8, 1, 2, 3, 4, 5, 6, 7, 8]), + hpcombi.PTransf16([1, 1, 2, 3, 4, 5, 6, 7, 8]), + ) + assert S.size() == 43_046_721 diff --git a/tests/test_presentation_examples.py b/tests/test_presentation_examples.py index a8ec638a..1fb703f6 100644 --- a/tests/test_presentation_examples.py +++ b/tests/test_presentation_examples.py @@ -13,6 +13,7 @@ import pytest from libsemigroups_pybind11 import ( + LIBSEMIGROUPS_EIGEN_ENABLED, POSITIVE_INFINITY, LibsemigroupsError, Presentation, @@ -456,5 +457,6 @@ def test_braid_group(): p = examples.braid_group(3) assert len(p.alphabet()) == 4 - tc = ToddCoxeter(congruence_kind.twosided, p) - assert tc.number_of_classes() == POSITIVE_INFINITY + if LIBSEMIGROUPS_EIGEN_ENABLED: + tc = ToddCoxeter(congruence_kind.twosided, p) + assert tc.number_of_classes() == POSITIVE_INFINITY