Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SchreierSims for v1 #200

Merged
merged 18 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ def no_doc_run(self):
("Transf", "Perm"),
],
"FroidurePinPBR": [(r"\bPBR\b", "Element")],
"SchreierSimsPerm1": [(r"\bPerm1\b", "Element")],
"ReversiblePaths": [(r"\bPaths\b", "ReversiblePaths")],
}

Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ See the installation instructions:
main-algorithms/knuth-bendix/index
main-algorithms/konieczny/index
main-algorithms/radoszewski-rytter/index
main-algorithms/schreier-sims/index
main-algorithms/stephen/index
main-algorithms/todd-coxeter/index
main-algorithms/ukkonen/index
Expand Down
19 changes: 19 additions & 0 deletions docs/source/main-algorithms/schreier-sims/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.. Copyright (c) 2024 Joseph Edwards

Distributed under the terms of the GPL license version 3.

The full license is in the file LICENSE, distributed with this software.

Schreier-Sims
=============

This page describes the functionality related to the Schreier-Sims algorithm for
computing a base and strong generating set of a permutation group in
james-d-mitchell marked this conversation as resolved.
Show resolved Hide resolved
``libsemigroups_pybind11``.


.. toctree::
:maxdepth: 1

schreier-sims
schreier-sims-helpers
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.. Copyright (c) 2024 Joseph Edwards

Distributed under the terms of the GPL license version 3.

The full license is in the file LICENSE, distributed with this software.

Schreier-Sims helper functions
==============================

This page contains the documentation for various helper functions for
manipulating :any:`SchreierSimsPerm1` objects.

Contents
--------

In ``libsemigroups_pybind11``:

.. currentmodule:: libsemigroups_pybind11.schreier_sims

.. autosummary::
:nosignatures:

intersection


Full API
--------

.. currentmodule:: libsemigroups_pybind11

.. automodule:: libsemigroups_pybind11.schreier_sims
:members:
:imported-members:
51 changes: 51 additions & 0 deletions docs/source/main-algorithms/schreier-sims/schreier-sims.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.. Copyright (c) 2023-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

The Schreier-Sims class
=======================

.. autoclass:: SchreierSimsPerm1
:doc-only:
:class-doc-from: class

Contents
--------

.. autosummary::
:nosignatures:

SchreierSimsPerm1.add_base_point
SchreierSimsPerm1.add_generator
SchreierSimsPerm1.base
SchreierSimsPerm1.base_size
SchreierSimsPerm1.contains
SchreierSimsPerm1.current_size
SchreierSimsPerm1.currently_contains
SchreierSimsPerm1.empty
SchreierSimsPerm1.finished
SchreierSimsPerm1.generator
SchreierSimsPerm1.init
SchreierSimsPerm1.inverse_transversal_element
SchreierSimsPerm1.number_of_generators
SchreierSimsPerm1.number_of_strong_generators
SchreierSimsPerm1.one
SchreierSimsPerm1.orbit_lookup
SchreierSimsPerm1.run
SchreierSimsPerm1.sift
SchreierSimsPerm1.sift_inplace
SchreierSimsPerm1.size
SchreierSimsPerm1.strong_generator
SchreierSimsPerm1.transversal_element

Full API
--------

.. autoclass:: SchreierSimsPerm1
:members:
:class-doc-from: init

1 change: 1 addition & 0 deletions etc/replace-strings-in-doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def dive(path):
"ImageRightActionPPerm1PPerm1": "ImageRightAction",
"_libsemigroups_pybind11.FroidurePinBase": "FroidurePinBase",
"FroidurePinPBR": "FroidurePin",
"SchreierSimsPerm1": "SchreierSims",
}
files = all_html_files(html_path)

Expand Down
1 change: 1 addition & 0 deletions libsemigroups_pybind11/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@
MatrixKind.__name__ = "MatrixKind"

from .froidure_pin import FroidurePin
from .schreier_sims import SchreierSims
2 changes: 1 addition & 1 deletion libsemigroups_pybind11/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Action(Runner): # pylint: disable=invalid-name, too-many-instance-attribu
src/action.cpp!
"""

py_to_cxx_type_dict = {
_py_to_cxx_type_dict = {
(BMat8, BMat8, ImageRightAction, side.right): _RightActionBMat8BMat8,
(BMat8, BMat8, ImageLeftAction, side.left): _LeftActionBMat8BMat8,
(PPerm, PPerm, ImageRightAction, side.right): {
Expand Down
10 changes: 5 additions & 5 deletions libsemigroups_pybind11/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def __init__(self: Self, **kwargs):
super().__init__(("Element", "Point"), **kwargs)

def _init_cxx_obj(self: Self, elt: Any, pt: Any) -> Any:
cpp_obj_t = self._cxx_obj_type_from(samples=(elt, pt))
if self._cxx_obj is None or not isinstance(self._cxx_obj, cpp_obj_t):
self._cxx_obj = cpp_obj_t()
cxx_obj_t = self._cxx_obj_type_from(samples=(elt, pt))
if self._cxx_obj is None or not isinstance(self._cxx_obj, cxx_obj_t):
self._cxx_obj = cxx_obj_t()
return self._cxx_obj

def __call__( # pylint: disable=inconsistent-return-statements
Expand Down Expand Up @@ -88,7 +88,7 @@ class ImageRightAction(_ImageAction):
* *Point* -- the type of the points acted on
"""

py_to_cxx_type_dict = {
_py_to_cxx_type_dict = {
(_BMat8, _BMat8): _ImageRightActionBMat8BMat8,
(PPerm, PPerm): {
(_PPerm1, _PPerm1): _ImageRightActionPPerm1PPerm1,
Expand All @@ -109,7 +109,7 @@ class ImageLeftAction(_ImageAction): # pylint: disable=invalid-name
* *Point* -- the type of the points acted on
"""

py_to_cxx_type_dict = {
_py_to_cxx_type_dict = {
(_BMat8, _BMat8): _ImageLeftActionBMat8BMat8,
(PPerm, PPerm): {
(_PPerm1, _PPerm1): _ImageLeftActionPPerm1PPerm1,
Expand Down
36 changes: 27 additions & 9 deletions libsemigroups_pybind11/detail/cxx_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __init__(self: Self, expected_kwargs, **kwargs):
# the next line ensures we get the values in the same order as in
# lookup
values = tuple(kwargs[x] for x in expected_kwargs)
lookup = self.py_to_cxx_type_dict
lookup = self._py_to_cxx_type_dict
if values in lookup:
for key, val in kwargs.items():
setattr(self, key, val)
Expand Down Expand Up @@ -103,19 +103,37 @@ def __repr__(self: Self) -> str:
return self._cxx_obj.__repr__()
return ""

def __copy__(self: Self) -> str:
if self._cxx_obj is not None:
if hasattr(self._cxx_obj, "__copy__"):
return self._cxx_obj.__copy__()
raise NotImplementedError(
f"{type(self._cxx_obj)} has no member named __copy__"
)
raise NameError("_cxx_obj has not been defined")

def __eq__(self: Self, that) -> bool:
if self._cxx_obj is not None:
if hasattr(self._cxx_obj, "__eq__"):
return self._cxx_obj.__eq__(that)
raise NotImplementedError(
f"{type(self._cxx_obj)} has no member named __eq__"
)
raise NameError("_cxx_obj has not been defined")

@property
def py_to_cxx_type_dict(self: Self) -> dict:
def _py_to_cxx_type_dict(self: Self) -> dict:
return self.__class__.__lookup

# TODO type annotations
@py_to_cxx_type_dict.setter
def py_to_cxx_type_dict(self: Self, value):
@_py_to_cxx_type_dict.setter
def _py_to_cxx_type_dict(self: Self, value):
# TODO check that value is a dict of the correct structure
self.__class__.__lookup = value

def _cxx_obj_type_from(self: Self, samples=(), types=()) -> Any:
py_types = tuple([type(x) for x in samples] + list(types))
lookup = self.py_to_cxx_type_dict
lookup = self._py_to_cxx_type_dict
if py_types not in lookup:
raise ValueError(
f"unexpected keyword argument combination {py_types}, "
Expand All @@ -124,10 +142,10 @@ def _cxx_obj_type_from(self: Self, samples=(), types=()) -> Any:
if not isinstance(lookup[py_types], dict):
return lookup[py_types]
lookup = lookup[py_types]
cpp_types = tuple([type(to_cxx(x)) for x in samples] + list(types))
if cpp_types not in lookup:
cxx_types = tuple([type(to_cxx(x)) for x in samples] + list(types))
if cxx_types not in lookup:
raise ValueError(
f"unexpected keyword argument combination {cpp_types}, "
f"unexpected keyword argument combination {cxx_types}, "
f"expected one of {lookup.keys()}"
)
return lookup[cpp_types]
return lookup[cxx_types]
19 changes: 13 additions & 6 deletions libsemigroups_pybind11/froidure_pin.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def wrapper(self, *args):


class FroidurePin(CxxWrapper): # pylint: disable=missing-class-docstring
py_to_cxx_type_dict = {
_py_to_cxx_type_dict = {
(_Transf1,): _FroidurePinTransf1,
(_Transf2,): _FroidurePinTransf2,
(_Transf4,): _FroidurePinTransf4,
Expand All @@ -123,7 +123,10 @@ class FroidurePin(CxxWrapper): # pylint: disable=missing-class-docstring
# C++ FroidurePin special methods
########################################################################

def __init__( # pylint: disable=super-init-not-called
# TODO(1): This __init__ is identical to the SchreierSims __init__. It would
# probably be best to make an abstract base class from which all classes
# that construct using a list of generators inherit.
def __init__( # pylint: disable=super-init-not-called, duplicate-code
self: Self, *args
) -> None:
if len(args) == 0:
Expand All @@ -132,12 +135,12 @@ def __init__( # pylint: disable=super-init-not-called
gens = args[0]
else:
gens = args
cpp_obj_t = self._cxx_obj_type_from(
cxx_obj_t = self._cxx_obj_type_from(
samples=(to_cxx(gens[0]),),
)
self.Element = type(gens[0])

self._cxx_obj = cpp_obj_t([to_cxx(x) for x in gens])
self._cxx_obj = cxx_obj_t([to_cxx(x) for x in gens])

@_returns_element
def __getitem__(self: Self, i: int) -> Element:
Expand All @@ -154,10 +157,14 @@ def __iter__(self: Self) -> Iterator:
########################################################################

def current_elements(self: Self) -> Iterator:
return map(lambda x: to_py(self.Element, x), self._cxx_obj.current_elements())
return map(
lambda x: to_py(self.Element, x), self._cxx_obj.current_elements()
)

def idempotents(self: Self) -> Iterator:
return map(lambda x: to_py(self.Element, x), self._cxx_obj.idempotents())
return map(
lambda x: to_py(self.Element, x), self._cxx_obj.idempotents()
)

def sorted_elements(self: Self) -> Iterator:
return map(
Expand Down
Loading