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
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ We would like to thank the authors and contributors of these projects!
main-algorithms/action/index
main-algorithms/congruence/index
main-algorithms/core-classes/index
main-algorithms/du-narendran-rusinowitch/index
main-algorithms/froidure-pin/index
main-algorithms/kambites/index
main-algorithms/knuth-bendix/index
Expand All @@ -165,4 +166,3 @@ We would like to thank the authors and contributors of these projects!
main-algorithms/schreier-sims/index
main-algorithms/stephen/index
main-algorithms/todd-coxeter/index

10 changes: 10 additions & 0 deletions docs/source/libsemigroups.bib
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,13 @@ @article{Stephen1987aa
author = {Stephen, Joseph Buchanan},
month = {01},
year = {1987}}

@article{Du2026aa,
author = {Du, Wei and Narendran, Paliath and Rusinowitch, Michael},
doi = {10.1016/j.jlamp.2026.101126},
journal = {Journal of Logical and Algebraic Methods in Programming},
pages = {101126},
title = {Inferring RPO symbol orderings},
volume = {150},
year = {2026}
}
17 changes: 17 additions & 0 deletions docs/source/main-algorithms/du-narendran-rusinowitch/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
..
Copyright (c) 2026 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

Du-Narendran-Rusinowitch
========================

This page describes the implementation of the algorithm from
:cite:`Du2026aa` for finding an alphabet order that orients the rules of a
presentation with respect to recursive-path ordering.

.. autofunction:: du_narendran_rusinowitch
83 changes: 83 additions & 0 deletions src/du-narendran-rusinowitch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// libsemigroups_pybind11
// Copyright (C) 2026 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 <http://www.gnu.org/licenses/>.
//

// C++ stl headers....
#include <string> // for string

// libsemigroups....
#include <libsemigroups/du-narendran-rusinowitch.hpp> // for du_narendran_...
#include <libsemigroups/presentation.hpp> // for Presentation
#include <libsemigroups/types.hpp> // for word_type

// pybind11....
#include <pybind11/pybind11.h> // for arg, module
#include <pybind11/stl.h> // for automatic conversions

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

namespace libsemigroups {
namespace py = pybind11;

namespace {
template <typename Word>
void bind_du_narendran_rusinowitch(py::module& m) {
m.def(
"du_narendran_rusinowitch",
[](Presentation<Word> const& p) {
return du_narendran_rusinowitch(p);
},
py::arg("p"),
R"pbdoc(
:sig=(p: Presentation) -> str | list[int]:
:only-document-once:
Find an alphabet order that orients every rule using recursive-path ordering.

This function returns the alphabet of *p*, ordered so that every rule
:math:`u \to v` satisfies :math:`u > v` with respect to recursive-path
ordering. It returns an empty word if no such order exists, or if the alphabet
of *p* is empty.

:param p: the presentation whose rules are to be oriented.
:type p: Presentation

:returns: An alphabet order orienting every rule, or an empty word if none
exists.
:rtype: str | list[int]

:raises LibsemigroupsError: if the alphabet or rules of *p* are invalid.

.. seealso::
:any:`rpo_cmp`

.. doctest::

>>> from libsemigroups_pybind11 import Presentation, du_narendran_rusinowitch
>>> p = Presentation("abcd")
>>> p.rules = ["a", "cc", "d", "bcc", "bccb", "c", "cccb", "bccc"]
>>> du_narendran_rusinowitch(p)
'bcda'
)pbdoc");
}
} // namespace

void init_du_narendran_rusinowitch(py::module& m) {
bind_du_narendran_rusinowitch<std::string>(m);
bind_du_narendran_rusinowitch<word_type>(m);
}
} // namespace libsemigroups
2 changes: 2 additions & 0 deletions src/libsemigroups_pybind11/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from .congruence import Congruence
from .detail.cxx_wrapper import wrap_cxx_free_fn as _wrap_cxx_free_fn
from .detail.dot import Dot
from .du_narendran_rusinowitch import du_narendran_rusinowitch
from .forest import PathsFromRoots, PathsToRoots
from .froidure_pin import FroidurePin
from .hpcombi import LIBSEMIGROUPS_HPCOMBI_ENABLED
Expand Down Expand Up @@ -244,6 +245,7 @@
"ToddCoxeter",
"Transf",
# Free functions from submodules
"du_narendran_rusinowitch",
"is_obviously_infinite",
"to",
"validate",
Expand Down
15 changes: 15 additions & 0 deletions src/libsemigroups_pybind11/du_narendran_rusinowitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2026 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.

"""Subpackage for importing and wrapping :any:`du_narendran_rusinowitch`."""

from _libsemigroups_pybind11 import du_narendran_rusinowitch as _du_narendran_rusinowitch

from .detail.cxx_wrapper import wrap_cxx_free_fn as _wrap_cxx_free_fn

du_narendran_rusinowitch = _wrap_cxx_free_fn(_du_narendran_rusinowitch)

__all__ = ["du_narendran_rusinowitch"]
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ namespace libsemigroups {
init_bipart(m);
init_blocks(m);
init_cong(m);
init_du_narendran_rusinowitch(m);
init_error(m);
init_freeband(m);
init_froidure_pin_base(m); // Must be before init_froidure_pin
Expand Down
1 change: 1 addition & 0 deletions src/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace libsemigroups {
void init_cong(py::module&);
void init_constants(py::module&);
void init_dot(py::module&);
void init_du_narendran_rusinowitch(py::module&);
void init_error(py::module&);
void init_forest(py::module&);
void init_freeband(py::module&);
Expand Down
60 changes: 60 additions & 0 deletions tests/test_du_narendran_rusinowitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (c) 2026 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.

"""Tests for du_narendran_rusinowitch."""

# pylint: disable=missing-function-docstring

import pytest

from libsemigroups_pybind11 import (
Alphabet,
LibsemigroupsError,
Presentation,
du_narendran_rusinowitch,
rpo_cmp,
)


@pytest.mark.parametrize(
("alphabet", "rules", "expected"),
[
("abcde", ["dbcbace", "cbbaec", "bcbad", "badbc"], "edcab"),
(
[0, 1, 2, 3, 4],
[[3, 1, 2, 1, 0, 2, 4], [2, 1, 1, 0, 4, 2], [1, 2, 1, 0, 3], [1, 0, 3, 1, 2]],
[4, 3, 2, 0, 1],
),
],
)
def test_du_narendran_rusinowitch(alphabet, rules, expected):
p = Presentation(alphabet)
p.rules = rules

assert du_narendran_rusinowitch(p) == expected
for lhs, rhs in list(zip(rules[::2], rules[1::2], strict=True)):
assert rpo_cmp(Alphabet(alphabet), rhs, lhs)


def test_du_narendran_rusinowitch_no_order_exists():
p = Presentation("abcd")
p.rules = ["a", "b", "b", "c", "c", "d", "d", "a"]

assert du_narendran_rusinowitch(p) == ""


def test_du_narendran_rusinowitch_empty_rules():
p = Presentation([2, 0, 1])

assert du_narendran_rusinowitch(p) == [2, 0, 1]


def test_du_narendran_rusinowitch_invalid_presentation():
p = Presentation("ab")
p.rules = ["a", "c"]

with pytest.raises(LibsemigroupsError):
du_narendran_rusinowitch(p)
Loading