diff --git a/examples/newpres.py b/examples/newpres.py new file mode 100644 index 0000000..e1a4d35 --- /dev/null +++ b/examples/newpres.py @@ -0,0 +1,48 @@ +import libsemigroups_cppyy + +from libsemigroups_cppyy.todd_coxeter import * + +def symgroup(n): + s = ["s%i"%i for i in range(n)] + res = [] + for i in range(1,n): + res.append([[s[i],s[i]], [1]]) + for i in range(1, n-1): + res.append([[s[i],s[i+1],s[i]], [s[i+1],s[i],s[i+1]]]) + for i in range(1, n-2): + for j in range(i+2, n): + res.append([[s[i],s[j]], [s[j],s[i]]]) + return s[1:], res +def Hecke0(n): + p = ["pi%i"%i for i in range(n)] + res = [] + for i in range(1,n): + res.append([[p[i],p[i]], [1]]) + for i in range(1, n-1): + res.append([[p[i],p[i+1],p[i]], [p[i+1],p[i],p[i+1]]]) + for i in range(1, n-2): + for j in range(i+2, n): + res.append([[p[i],p[j]], [p[j],p[i]]]) + return p[1:], res + + +N = 5 + +gs, rs = symgroup(N) +SG = make_cong([1]+gs, rs, identity=1) +SG.run() +SG.standardize(ToddCoxeter.order.shortlex) +clSG = classes_reduced_word_cong(SG) + +gs, rs = Hecke0(N) +H0 = make_cong([1]+gs, rs, identity=1) +H0.run() +H0.standardize(ToddCoxeter.order.shortlex) +clH0 = classes_reduced_word_cong(H0) + +print (clSG == clH0) + + + + + diff --git a/libsemigroups_cppyy/todd_coxeter.py b/libsemigroups_cppyy/todd_coxeter.py new file mode 100644 index 0000000..2d0346e --- /dev/null +++ b/libsemigroups_cppyy/todd_coxeter.py @@ -0,0 +1,75 @@ +""" +This file contains the interface to the implementation of the +Todd-Coxeter algorithm for finitely presented semigroups in libsemigroups; see + + https://libsemigroups.readthedocs.io/en/latest/_generated/libsemigroups__congruence__toddcoxeter.html + +for further details. + + +""" +import cppyy +cppyy.include("cong.hpp") +cppyy.include("cong-intf.hpp") + +lsg = cppyy.gbl.libsemigroups +congruence_type = lsg.congruence_type +ToddCoxeter = lsg.congruence.ToddCoxeter + +def make_cong(lgen, rels, identity=None): + """ + Build a congruence + + each generator must be hashable. + + - `lgen` : the list of generators (must be unique) + - `rels` : a list of pairs of tuple + - `identity` : the identity if known + + if `rels` contains a generator which is not in `lgen`, it will be appended. + """ + if len(set(lgen)) != len(lgen): + raise ValueError("duplicate generator") + if identity is not None and identity not in lgen: + lgen.append(identity) + for rel in rels: + for w in rel: + for g in w: + if g not in lgen: + lgen.append(g) + res = ToddCoxeter(lsg.congruence_type.twosided) + res.set_nr_generators(len(lgen)) + def add_pair(a,b) : + # print("Adding : (%s, %s)"%(a,b)) + res.add_pair(a,b) + if identity is not None: + idd = lgen.index(identity) + for ig in range(len(lgen)): + add_pair([ig, idd], [ig]) + if idd != ig: + add_pair([idd, ig], [ig]) + def iword(w): + return [lgen.index(l) for l in w] + for rel in rels: + trg = iword(rel[0]) + for src in rel[1:]: + add_pair(trg, iword(src)) + return res + + +def classes_reduced_word_cong(C): + r""" + returns the list of the classes all the reduced words. + + Each classes is stored as a frozenset. in the same order as the + index of `C`. + """ + res = [] + for nc in range(C.nr_classes()): + w = C.class_index_to_word(nc) + Cl = [] + for wrd in lsg.shortlex_words(C.nr_generators(), w.size(), w.size()): + if (C.word_to_class_index(wrd) == nc): + Cl.append(tuple(wrd)) + res.append(frozenset(Cl)) + return res