Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
19 changes: 15 additions & 4 deletions src/libsemigroups_pybind11/presentation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
InversePresentationWord as _InversePresentationWord,
PresentationString as _PresentationString,
PresentationWord as _PresentationWord,
RulesString as _RulesString,
RulesWord as _RulesWord,
presentation_add_cyclic_conjugates as _add_cyclic_conjugates,
presentation_add_identity_rules as _add_identity_rules,
presentation_add_inverse_rules as _add_inverse_rules,
Expand Down Expand Up @@ -160,14 +162,23 @@ def __init__(self: _Self, *args, **kwargs) -> None:

@_copydoc(_PresentationWord.rules)
@property
def rules(self: _Self) -> list[list[int] | str]:
def rules(self: _Self) -> list[list[int]] | list[str]:
# pylint: disable=missing-function-docstring
return _to_cxx(self).rules

@rules.setter
def rules(self: _Self, val: list[list[int] | str]) -> None:
_to_cxx(self).rules = val

def rules(self: _Self, val: list[list[int]] | list[str]) -> None:
if self.py_template_params == (list[int],):
_to_cxx(self).rules = _RulesWord(val)
else:
_to_cxx(self).rules = _RulesString(val)


# HACK: The next line is a hack to make _RulesString objects to look like
# lists, for some reason _RulesWord doesn't have this problem. It looks like
# _RulesString has a __repr__ function bound in pybind11::bind_vector and
# defining it again has no effect.
_RulesString.__repr__ = lambda self: repr(list(self))

_copy_cxx_mem_fns(_PresentationWord, Presentation)
_register_cxx_wrapped_type(_PresentationWord, Presentation)
Expand Down
112 changes: 111 additions & 1 deletion src/present.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <libsemigroups/presentation.hpp> // for Presentation
#include <libsemigroups/ranges.hpp> // for is_sorted
#include <libsemigroups/types.hpp> // for word_type
#include <libsemigroups/word-range.hpp> // for operator+

// pybind11....
#include <pybind11/cast.h> // for arg
Expand All @@ -38,14 +39,116 @@
#include <pybind11/pybind11.h> // for class_, init, module
#include <pybind11/pytypes.h> // for sequence, str_attr_accessor
#include <pybind11/stl.h> // for std::vector conversion
#include <pybind11/stl_bind.h> // for bind_vector

// libsemigroups_pybind11....
#include "main.hpp" // for init_present

PYBIND11_MAKE_OPAQUE(std::vector<std::string>);
PYBIND11_MAKE_OPAQUE(std::vector<libsemigroups::word_type>);

namespace libsemigroups {
namespace py = pybind11;

namespace {
// TODO there are probably more functions like the vector_* functions
// below that could be implemented.

[[nodiscard]] bool
vector_equals_sequence(std::vector<word_type> const& self,
py::object other) {
if (py::isinstance<py::sequence>(other)) {
py::sequence other_seq = other.cast<py::sequence>();

if (other_seq.size() != self.size()) {
return false;
}

for (size_t i = 0; i < self.size(); ++i) {
py::sequence other_item_seq = other_seq[i].cast<py::sequence>();
word_type const& self_item = self[i];
if (other_item_seq.size() != self_item.size()) {
return false;
}
for (size_t j = 0; j < self_item.size(); ++j) {
if (self_item[j] != other_item_seq[j].cast<letter_type>()) {
return false;
}
}
}
return true;
}
return false;
}

[[nodiscard]] bool
vector_equals_sequence(std::vector<std::string> const& self,
py::object other) {
if (py::isinstance<py::sequence>(other)) {
py::sequence other_seq = other.cast<py::sequence>();

if (other_seq.size() != self.size()) {
return false;
}

for (size_t i = 0; i < self.size(); ++i) {
if (self[i] != other_seq[i].cast<std::string>()) {
return false;
}
}
return true;
}
return false;
}

template <typename Word>
[[nodiscard]] std::vector<Word>
vector_add_sequence(std::vector<Word> const& self, py::object other) {
if (!py::isinstance<py::sequence>(other)) {
throw py::type_error("unsupported operand type(s) for +");
}
py::sequence other_seq = other.cast<py::sequence>();

std::vector<Word> result(self);
result.reserve(self.size() + other_seq.size());

for (auto const& item : other_seq) {
result.push_back(item.cast<Word>());
}

return result;
}

template <typename Word>
[[nodiscard]] std::vector<Word>
vector_add_vector(std::vector<Word> const& self,
std::vector<Word> const& other) {
std::vector<Word> result(self);
result.insert(result.end(), other.begin(), other.end());
return result;
}

template <typename Word>
void bind_vector(py::module& m, std::string const& name) {
py::bind_vector<std::vector<Word>>(
m, name.c_str(), py::module_local(false))
.def("__repr__",
[](std::vector<Word> const& self) {
return fmt::format("[{}]",
fmt::join(self.begin(), self.end(), ", "));
})
.def("__eq__",
[](std::vector<Word> const& self, py::object other) {
return vector_equals_sequence(self, other);
})
.def("__ne__",
[](std::vector<Word> const& self, py::object other) {
return !vector_equals_sequence(self, other);
})
.def("__add__", &vector_add_sequence<Word>)
.def("__add__", &vector_add_vector<Word>);
}

template <typename Word>
void bind_present(py::module& m, std::string const& name) {
using Presentation_ = Presentation<Word>;
Expand All @@ -71,18 +174,22 @@ available in the module :any:`libsemigroups_pybind11.presentation`.)pbdoc");
[](Presentation_ const& lhop, Presentation_ rhop) -> bool {
return lhop == rhop;
});

thing.def_readwrite("rules",
&Presentation_::rules,
R"pbdoc(
Data member holding the rules of the presentation.

The rules can be altered using the member functions of ``list``, and the
presentation can be checked for validity using :any:`throw_if_bad_alphabet_or_rules`.)pbdoc");
presentation can be checked for validity using
:any:`throw_if_bad_alphabet_or_rules`.)pbdoc");

thing.def(py::init<>(), R"pbdoc(
:sig=(self: Presentation) -> None:
Default constructor.

Constructs an empty presentation with no rules and no alphabet.)pbdoc");

thing.def(
"copy",
[](Presentation_ const& self) { return Presentation_(self); },
Expand All @@ -94,6 +201,7 @@ Copy a :any:`Presentation` object.
:returns: A copy.
:rtype: Presentation
)pbdoc");

thing.def("__copy__",
[](Presentation_ const& that) { return Presentation_(that); });
thing.def(
Expand Down Expand Up @@ -1658,6 +1766,8 @@ defined in the alphabet, and that the inverses act as semigroup inverses.
} // namespace

void init_present(py::module& m) {
bind_vector<word_type>(m, "RulesWord");
bind_vector<std::string>(m, "RulesString");
bind_present<word_type>(m, "PresentationWord");
bind_present<std::string>(m, "PresentationString");
}
Expand Down
2 changes: 1 addition & 1 deletion src/transf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ fewer points requiring less space per point.
m.def("transf_inverse",
py::overload_cast<Perm_ const&>(&inverse<N, Scalar>));
} // bind_perm
} // namespace
} // namespace

void init_transf(py::module& m) {
// Transformations
Expand Down
Loading