diff --git a/doc/dual.xml b/doc/dual.xml new file mode 100644 index 0000000000..7ca5ef39da --- /dev/null +++ b/doc/dual.xml @@ -0,0 +1,135 @@ +############################################################################# +## +#W dual.xml +#Y Copyright (C) 2018 Finn Smith +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +<#GAPDoc Label="DualSemigroup"> + + + The dual semigroup of the given semigroup. + + The dual semigroup of a semigroup S is the + anti-isomorphic semigroup with the same underlying set as S, + where multiplication is reversed. This attribute returns a semigroup + isomorphic to the dual semigroup of S. + + S := Semigroup([Transformation([1, 4, 3, 2, 2]), +> Transformation([5, 4, 4, 1, 2])]);; +gap> D := DualSemigroup(S); +> +gap> Size(S) = Size(D); +true +gap> NrDClasses(S) = NrDClasses(D); +true]]> +<#/GAPDoc> + +<#GAPDoc Label="AntiIsomorphismDualSemigroup"> + + + + An anti-isomorphism from S to the corresponding dual semigroup. + + + The dual semigroup of S mathematically has the same underlying + set as S, but is represented with a different set of elements in + &Semigroups;. This function returns a mapping which is an anti-isomorphism from + S to its dual. + + S := PartitionMonoid(3); + +gap> map := AntiIsomorphismDualSemigroup(S); +MappingByFunction( , + >, function( x ) ... end, function( x ) ... end ) +gap> inv := InverseGeneralMapping(map);; +gap> x := Bipartition([[1, -2], [2, -3], [3, -1]]); + +gap> y := Bipartition([[1], [2, -2], [3, -3], [-1]]); + +gap> (x ^ map) * (y ^ map) = (y * x) ^ map; +true +gap> x ^ map; +< + in the dual semigroup>]]> +<#/GAPDoc> + +<#GAPDoc Label="IsDualSemigroupElement"> + + + Returns true if elt has the representation of a dual + semigroup element. + + Elements of a dual semigroup obtained using + normally lie in this + category. The exception is elements obtained by applying + the map to elements already + in this category. That is, the elements of a semigroup lie in the + category if and only if the + elements of the corresponding dual semigroup do not. + + S := SingularPartitionMonoid(4);; +gap> D := DualSemigroup(S);; +gap> s := GeneratorsOfSemigroup(S)[1];; +gap> map := AntiIsomorphismDualSemigroup(S);; +gap> t := s ^ map; +< + in the dual semigroup> +gap> IsDualSemigroupElement(t); +true +gap> inv := InverseGeneralMapping(map);; +gap> x := t ^ inv; + +gap> IsDualSemigroupElement(x); +false]]> +<#/GAPDoc> + +<#GAPDoc Label="IsDualSemigroupRep"> + + + Returns true if sgrp is represented as + a dual semigroup. + + Semigroups created using + normally have this representation. The exception is semigroups + which are the dual of semigroups already lying in this category. + That is, a semigroup has the representation + if and only if the corresponding + dual semigroup does not. + + S := Semigroup([Transformation([3, 5, 1, 1, 2]), +> Transformation([1, 2, 4, 4, 3])]); + +gap> D := DualSemigroup(S); +> +gap> IsDualSemigroupRep(D); +true +gap> R := DualSemigroup(D); + +gap> IsDualSemigroupRep(R); +false +gap> R = S; +true +gap> T := Range(IsomorphismTransformationSemigroup(D)); + +gap> IsDualSemigroupRep(T); +false +gap> x := Representative(D); + +gap> V := Semigroup(x); +> +gap> IsDualSemigroupRep(V); +true]]> +<#/GAPDoc> diff --git a/doc/z-chap06.xml b/doc/z-chap06.xml index e2d3f81fd4..4dab708e2e 100644 --- a/doc/z-chap06.xml +++ b/doc/z-chap06.xml @@ -360,6 +360,37 @@ true]]> <#Include Label = "InverseSubsemigroupByProperty"> <#Include Label = "DirectProduct"> <#Include Label = "WreathProduct"> + + + +
+ Dual semigroups + Given a semigroup (S, \cdot), we can consider the anti-isomorphic + semigroup (S, \ast) where we define: + + s \ast t = t \cdot s for all s, t \in S. + + This is called the dual semigroup of (S, \cdot). + + The Green's relations of the dual semigroup D of S + are, appropriately, dual to those of S. Given &x; and &y; in S: + + + &x; and &y; are &L;-related in D if and only if + they are &R;-related in S, and vice-versa, + &x; and &y; are &H;-, &D;-, or &J;-related in D if and + only if they are &H;-, &D;-, or &J;-related in S respectively. + + + + While mathematically a semigroup and its dual have the same underlying set, + in &Semigroups; the underlying sets are disjoint. This makes it reasonable + to speak of the dual semigroup D of S. + + <#Include Label = "DualSemigroup"> + <#Include Label = "IsDualSemigroupRep"> + <#Include Label = "IsDualSemigroupElement"> + <#Include Label = "AntiIsomorphismDualSemigroup">
diff --git a/gap/attributes/dual.gd b/gap/attributes/dual.gd new file mode 100644 index 0000000000..4c2585a6bf --- /dev/null +++ b/gap/attributes/dual.gd @@ -0,0 +1,28 @@ +############################################################################# +## +## dual.gd +## Copyright (C) 2018 James D. Mitchell +## Finn Smith +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +DeclareCategory("IsDualSemigroupElement", IsAssociativeElement); +DeclareCategoryCollections("IsDualSemigroupElement"); +DeclareAttribute("DualSemigroup", IsSemigroup); + +# Every semigroup is mathematically a dual semigroup +# What we care about is whether it is represented as one +DeclareRepresentation("IsDualSemigroupRep", + IsEnumerableSemigroupRep and + IsDualSemigroupElementCollection, + []); + +DeclareAttribute("DualSemigroupOfFamily", IsFamily); +DeclareAttribute("AntiIsomorphismDualSemigroup", IsSemigroup); +DeclareGlobalFunction("UnderlyingElementOfDualSemigroupElement"); + +InstallTrueMethod(IsDualSemigroupRep, + IsSemigroup and IsDualSemigroupElementCollection); diff --git a/gap/attributes/dual.gi b/gap/attributes/dual.gi new file mode 100644 index 0000000000..95314f52e5 --- /dev/null +++ b/gap/attributes/dual.gi @@ -0,0 +1,210 @@ +############################################################################# +## +## dual.gi +## Copyright (C) 2018 James D. Mitchell +## Finn Smith +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## +## This file contains an implementation of dual semigroups. We only provide +## enough functionality to allow dual semigroups to work as enumerable +## semigroups. This is to avoid having to install versions of every function +## in Semigroups specially for dual semigroup representations. In some cases +## special functions would be faster. + +InstallMethod(DualSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + local dual, fam, filts, map, type; + + if IsDualSemigroupRep(S) then + if HasGeneratorsOfSemigroup(S) then + return Semigroup(List(GeneratorsOfSemigroup(S), + x -> UnderlyingElementOfDualSemigroupElement(x))); + fi; + ErrorNoReturn("Semigroups: DualSemigroup: \n", + "this dual semigroup cannot be constructed ", + "without knowing generators,"); + fi; + + fam := NewFamily("DualSemigroupElementsFamily", IsDualSemigroupElement); + dual := Objectify(NewType(CollectionsFamily(fam), + IsWholeFamily and + IsDualSemigroupRep and + IsAttributeStoringRep), + rec()); + + filts := IsDualSemigroupElement; + if IsMultiplicativeElementWithOne(Representative(S)) then + filts := filts and IsMultiplicativeElementWithOne; + fi; + + type := NewType(fam, filts); + fam!.type := type; + + SetDualSemigroupOfFamily(fam, dual); + + SetElementsFamily(FamilyObj(dual), fam); + SetDualSemigroup(dual, S); + + if HasIsFinite(S) then + SetIsFinite(dual, IsFinite(S)); + fi; + + if IsTransformationSemigroup(S) then + map := AntiIsomorphismDualSemigroup(dual); + SetAntiIsomorphismTransformationSemigroup(dual, map); + fi; + + if HasGeneratorsOfSemigroup(S) then + SetGeneratorsOfSemigroup(dual, + List(GeneratorsOfSemigroup(S), + x -> SEMIGROUPS.DualSemigroupElementNC(dual, + x))); + fi; + + if HasGeneratorsOfMonoid(S) then + SetGeneratorsOfMonoid(dual, + List(GeneratorsOfMonoid(S), + x -> SEMIGROUPS.DualSemigroupElementNC(dual, + x))); + fi; + return dual; +end); + +SEMIGROUPS.DualSemigroupElementNC := function(S, s) + if not IsDualSemigroupElement(s) then + return Objectify(ElementsFamily(FamilyObj(S))!.type, [s]); + fi; + return s![1]; +end; + +InstallMethod(AntiIsomorphismDualSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + local dual, inv, iso; + + dual := DualSemigroup(S); + iso := function(x) + return SEMIGROUPS.DualSemigroupElementNC(dual, x); + end; + + inv := function(x) + return SEMIGROUPS.DualSemigroupElementNC(S, x); + end; + return MappingByFunction(S, dual, iso, inv); +end); + +InstallGlobalFunction(UnderlyingElementOfDualSemigroupElement, +"for a dual semigroup element", +function(s) + if not IsDualSemigroupElement(s) then + ErrorNoReturn("Semigroups: UnderlyingElementOfDualSemigroupElement: \n", + "the argument must be an element represented as a dual ", + "semigroup element,"); + fi; + return s![1]; +end); + +################################################################################ +## Technical methods +################################################################################ + +InstallMethod(OneMutable, "for a dual semigroup element", +[IsDualSemigroupElement and IsMultiplicativeElementWithOne], +function(s) + local S, x; + S := DualSemigroupOfFamily(FamilyObj(s)); + x := SEMIGROUPS.DualSemigroupElementNC(DualSemigroup(S), s); + return SEMIGROUPS.DualSemigroupElementNC(S, OneMutable(x)); +end); + +InstallMethod(MultiplicativeNeutralElement, "for a dual semigroup", +[IsDualSemigroupRep], +10, # add rank to beat enumeration methods +function(S) + local m; + m := MultiplicativeNeutralElement(DualSemigroup(S)); + if m <> fail then + return SEMIGROUPS.DualSemigroupElementNC(S, m); + fi; + return fail; +end); + +InstallMethod(Representative, "for a dual semigroup", +[IsDualSemigroupRep], +function(S) + if HasGeneratorsOfSemigroup(S) then + return GeneratorsOfSemigroup(S)[1]; + fi; + return SEMIGROUPS.DualSemigroupElementNC(S, Representative(DualSemigroup(S))); +end); + +InstallMethod(Size, "for a dual semigroup", +[IsDualSemigroupRep], +10, # add rank to beat enumeration methods +function(S) + return Size(DualSemigroup(S)); +end); + +InstallMethod(AsList, "for a dual semigroup", +[IsDualSemigroupRep], +10, # add rank to beat enumeration methods +function(S) + return List(DualSemigroup(S), s -> SEMIGROUPS.DualSemigroupElementNC(S, s)); +end); + +InstallMethod(\*, "for dual semigroup elements", +IsIdenticalObj, +[IsDualSemigroupElement, IsDualSemigroupElement], +function(x, y) + return Objectify(FamilyObj(x)!.type, [y![1] * x![1]]); +end); + +InstallMethod(\=, "for dual semigroup elements", +IsIdenticalObj, +[IsDualSemigroupElement, IsDualSemigroupElement], +function(x, y) + return x![1] = y![1]; +end); + +InstallMethod(\<, "for dual semigroup elements", +IsIdenticalObj, +[IsDualSemigroupElement, IsDualSemigroupElement], +function(x, y) + return x![1] < y![1]; +end); + +InstallMethod(ViewObj, "for dual semigroup elements", +[IsDualSemigroupElement], PrintObj); + +InstallMethod(PrintObj, "for dual semigroup elements", +[IsDualSemigroupElement], +function(x) + Print("<", ViewString(x![1]), " in the dual semigroup>"); +end); + +InstallMethod(ViewObj, "for a dual semigroup", +[IsDualSemigroupRep], PrintObj); + +InstallMethod(PrintObj, "for a dual semigroup", +[IsDualSemigroupRep], +function(S) + Print(""); +end); + +InstallMethod(ChooseHashFunction, "for a dual semigroup element and int", +[IsDualSemigroupElement, IsInt], +function(x, data) + local H, hashfunc; + + H := ChooseHashFunction(x![1], data); + hashfunc := function(a, b) + return H.func(a![1], b); + end; + return rec(func := hashfunc, data := H.data); +end); diff --git a/gap/tools/utils.gi b/gap/tools/utils.gi index f80efd6815..1f800245a9 100644 --- a/gap/tools/utils.gi +++ b/gap/tools/utils.gi @@ -242,6 +242,7 @@ SEMIGROUPS.DocXMLFiles := ["../PackageInfo.g", "congrees.xml", "congrms.xml", "conguniv.xml", + "dual.xml", "display.xml", "elements.xml", "factor.xml", diff --git a/init.g b/init.g index 484b020db4..afd68288ad 100644 --- a/init.g +++ b/init.g @@ -97,12 +97,13 @@ ReadPackage("semigroups", "gap/tools/iterators.gd"); ReadPackage("semigroups", "gap/attributes/attr.gd"); ReadPackage("semigroups", "gap/attributes/attract.gd"); ReadPackage("semigroups", "gap/attributes/attrinv.gd"); +ReadPackage("semigroups", "gap/attributes/dual.gd"); ReadPackage("semigroups", "gap/attributes/factor.gd"); ReadPackage("semigroups", "gap/attributes/isomorph.gd"); +ReadPackage("semigroups", "gap/attributes/isorms.gd"); ReadPackage("semigroups", "gap/attributes/maximal.gd"); ReadPackage("semigroups", "gap/attributes/normalizer.gd"); ReadPackage("semigroups", "gap/attributes/properties.gd"); -ReadPackage("semigroups", "gap/attributes/isorms.gd"); ReadPackage("semigroups", "gap/congruences/congpairs.gd"); ReadPackage("semigroups", "gap/congruences/congrms.gd"); diff --git a/read.g b/read.g index fc3c00a9a0..9a49a3b69e 100644 --- a/read.g +++ b/read.g @@ -73,12 +73,13 @@ ReadPackage("semigroups", "gap/attributes/attr.gi"); ReadPackage("semigroups", "gap/attributes/attract.gi"); ReadPackage("semigroups", "gap/attributes/attrfp.gi"); ReadPackage("semigroups", "gap/attributes/attrinv.gi"); +ReadPackage("semigroups", "gap/attributes/dual.gi"); ReadPackage("semigroups", "gap/attributes/factor.gi"); ReadPackage("semigroups", "gap/attributes/isomorph.gi"); +ReadPackage("semigroups", "gap/attributes/isorms.gi"); ReadPackage("semigroups", "gap/attributes/maximal.gi"); ReadPackage("semigroups", "gap/attributes/normalizer.gi"); ReadPackage("semigroups", "gap/attributes/properties.gi"); -ReadPackage("semigroups", "gap/attributes/isorms.gi"); ReadPackage("semigroups", "gap/congruences/congpairs.gi"); ReadPackage("semigroups", "gap/congruences/congrms.gi"); diff --git a/tst/standard/dual.tst b/tst/standard/dual.tst new file mode 100644 index 0000000000..13fc01dc33 --- /dev/null +++ b/tst/standard/dual.tst @@ -0,0 +1,313 @@ +############################################################################ +## +#W standard/dual.tst +#Y Copyright (C) 2018 Finn Smith +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## +gap> START_TEST("Semigroups package: standard/dual.tst"); +gap> LoadPackage("semigroups", false);; + +# +gap> SEMIGROUPS.StartTest();; + +# helper functions +gap> BruteForceAntiIsoCheck := function(antiiso) +> local x, y; +> if not IsInjective(antiiso) or not IsSurjective(antiiso) then +> return false; +> fi; +> for x in Generators(Source(antiiso)) do +> for y in Generators(Source(antiiso)) do +> if x ^ antiiso * y ^ antiiso <> (y * x) ^ antiiso then +> return false; +> fi; +> od; +> od; +> return true; +> end;; +gap> BruteForceInverseCheck := function(map) +> local inv; +> inv := InverseGeneralMapping(map); +> return ForAll(Source(map), x -> x = (x ^ map) ^ inv) +> and ForAll(Range(map), x -> x = (x ^ inv) ^ map); +> end;; + +#T# Creation of dual semigroups and elements - 1 +gap> S := Semigroup([Transformation([1, 3, 2]), Transformation([1, 4, 4, 2])]); + +gap> T := DualSemigroup(S); +> +gap> S := Semigroup([Transformation([1, 3, 2]), Transformation([1, 4, 4, 2])]);; +gap> T := DualSemigroup(S); +> +gap> AsSSortedList(T); +[ , + , + , + , + , + ] +gap> Size(T); +6 + +#T# Creation of dual semigroups and elements - 2 +gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]), +> Transformation([5, 5, 6, 1, 4, 5]), +> Transformation([5, 3, 1, 6, 4, 5])]);; +gap> T := DualSemigroup(S); +> +gap> Size(T); +385 +gap> Size(T) = Size(S); +true +gap> AsSortedList(T) = AsSortedList(List(S, +> s -> SEMIGROUPS.DualSemigroupElementNC(T, s))); +true +gap> iso := AntiIsomorphismDualSemigroup(S);; +gap> AsSortedList(T) = AsSortedList(List(S, +> s -> s ^ iso)); +true + +#T# Creation of dual semigroups and elements - 3 +gap> S := FullTransformationMonoid(20);; +gap> T := DualSemigroup(S);; +gap> HasGeneratorsOfSemigroup(T); +true +gap> HasGeneratorsOfMonoid(T); +true + +#T# DClasses of dual semigroups - 1 +gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]), +> Transformation([5, 5, 6, 1, 4, 5]), +> Transformation([5, 3, 1, 6, 4, 5])]);; +gap> T := DualSemigroup(S);; +gap> DClasses(T); +[ >, >, + >, >, + >, >, + >, >, + >, >, + >, >, + >, >, + > ] +gap> DS := AsSortedList(DClasses(T));; +gap> for i in [1 .. Size(DS) - 1] do +> if not DS[i] < DS[i + 1] then +> Print("comparison failure"); +> fi; +> od; + +#T# Green's classes of dual semigroups +gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]), +> Transformation([5, 5, 6, 1, 4, 5])]);; +gap> T := DualSemigroup(S);; +gap> iso := AntiIsomorphismDualSemigroup(T);; +gap> ForAll(DClasses(T), +> x -> AsSortedList(List(x, y -> y ^ iso)) = +> AsSortedList(GreensDClassOfElement(S, +> Representative(x) ^ iso))); +true +gap> ForAll(LClasses(T), +> x -> AsSortedList(List(x, y -> y ^ iso)) = +> AsSortedList(GreensRClassOfElement(S, +> Representative(x) ^ iso))); +true +gap> ForAll(RClasses(T), +> x -> AsSortedList(List(x, y -> y ^ iso)) = +> AsSortedList(GreensLClassOfElement(S, +> Representative(x) ^ iso))); +true +gap> ForAll(HClasses(T), +> x -> AsSortedList(List(x, y -> y ^ iso)) = +> AsSortedList(GreensHClassOfElement(S, +> Representative(x) ^ iso))); +true + +#T# Representatives +gap> S := FullTransformationMonoid(20);; +gap> T := DualSemigroup(S);; +gap> Representative(T); + + +#T# Size +gap> S := FullTransformationMonoid(20);; +gap> T := DualSemigroup(S);; +gap> Size(T); +104857600000000000000000000 + +#T# AsList +gap> S := FullTransformationMonoid(6);; +gap> T := DualSemigroup(S);; +gap> AsList(T);; + +#T# One and MultiplicativeNeutralElement - 1 +gap> S := FullBooleanMatMonoid(5);; +gap> T := DualSemigroup(S);; +gap> One(Representative(T)); + +gap> One(Representative(T)) = MultiplicativeNeutralElement(T); +true + +#T# One and MultiplicativeNeutralElement - 2 +gap> S := Semigroup([Transformation([2, 6, 3, 2, 4, 2]), +> Transformation([5, 5, 6, 1, 4, 5])]);; +gap> IsMonoidAsSemigroup(S); +false +gap> T := DualSemigroup(S);; +gap> One(Representative(T)); + +gap> MultiplicativeNeutralElement(T); +fail + +#T# AntiIsomorphisms +gap> S := FullTransformationMonoid(5);; +gap> T := DualSemigroup(S);; +gap> HasAntiIsomorphismTransformationSemigroup(T); +true +gap> Range(AntiIsomorphismTransformationSemigroup(T)) = S; +true +gap> antiso := AntiIsomorphismDualSemigroup(S); +MappingByFunction( , >, function( x ) ... end, function( \ +x ) ... end ) +gap> inv := AntiIsomorphismTransformationSemigroup(T); +MappingByFunction( \ +>, , function( x ) ... end, function( \ +x ) ... end ) +gap> ForAll(S, x -> (x ^ antiso) ^ inv = x); +true +gap> invantiso := InverseGeneralMapping(antiso); +MappingByFunction( \ +>, , function( x ) ... end, function( \ +x ) ... end ) +gap> ForAll(S, x -> (x ^ antiso) ^ invantiso = x); +true + +#T# AntiIsomorphism brute force checking - 1 +gap> S := FullTropicalMaxPlusMonoid(2, 4);; +gap> antiiso := AntiIsomorphismDualSemigroup(S);; +gap> BruteForceAntiIsoCheck(antiiso); +true +gap> BruteForceInverseCheck(antiiso); +true + +#T# AntiIsomorphism brute force checking - 2 +gap> S := PlanarPartitionMonoid(3);; +gap> antiiso := AntiIsomorphismDualSemigroup(S);; +gap> BruteForceAntiIsoCheck(antiiso); +true +gap> BruteForceInverseCheck(antiiso); +true + +#T# AntiIsomorphism brute force checking - 3 +gap> S := OrderEndomorphisms(6);; +gap> antiiso := AntiIsomorphismDualSemigroup(S);; +gap> BruteForceAntiIsoCheck(antiiso); +true +gap> BruteForceInverseCheck(antiiso); +true + +#T# Subsemigroups of a dual semigroup +gap> S := FullBooleanMatMonoid(4); + +gap> T := DualSemigroup(S); +> +gap> U := Semigroup(GeneratorsOfSemigroup(T){[3 .. 5]}); +> +gap> AsList(U); +[ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ] +gap> Size(last); +26 +gap> Size(DualSemigroup(U)); +26 +gap> V := DualSemigroup(U); + +gap> V = Semigroup(GeneratorsOfSemigroup(S){[3 .. 5]}); +true + +#T# UnderlyingElementOfDualSemigroupElement +gap> S := SingularPartitionMonoid(4);; +gap> D := DualSemigroup(S);; +gap> d := Representative(D); +< + in the dual semigroup> +gap> UnderlyingElementOfDualSemigroupElement(d); + +gap> Representative(S); + +gap> UnderlyingElementOfDualSemigroupElement(S); +Error, Semigroups: UnderlyingElementOfDualSemigroupElement: +the argument must be an element represented as a dual semigroup element, +gap> T := Semigroup(GeneratorsOfSemigroup(D){[1000 .. 2000]}); +> +gap> UnderlyingElementOfDualSemigroupElement(Representative(T)); + + +#T# UnbindVariables +gap> Unbind(antiso); +gap> Unbind(inv); +gap> Unbind(invantiso); +gap> Unbind(i); +gap> Unbind(U); +gap> Unbind(V); +gap> Unbind(S); +gap> Unbind(T); + +# +gap> SEMIGROUPS.StopTest(); +gap> STOP_TEST("Semigroups package: standard/dual.tst");