From 4b723e17f60dd9c3fb2d5af0e6d698f0b08884c1 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Wed, 25 Oct 2017 16:18:35 +0200 Subject: [PATCH] add direct prodcts --- gap/bytable.gi | 59 ++++++++++++++++++++++++ gap/structure.gi | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/gap/bytable.gi b/gap/bytable.gi index 8b40927..0e1b438 100644 --- a/gap/bytable.gi +++ b/gap/bytable.gi @@ -394,3 +394,62 @@ InstallMethod(Opposite, "for right rack", [ IsRightRack and IsBuiltFromMultiplicationTable], L -> LeftRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L))) ); + +# Note that opposite of quandles seems to come for free from above, which is +# good. + +## Direct products +# As in the case of general one-sided quasigroups, this implementation should +# only be called with a second-argument collection which is not a group or +# quasigroup. +InstallOtherMethod(DirectProductOp, + "for a list and non-quasigroup magma built form multiplication table", + [IsList, IsMagma and IsBuiltFromMultiplicationTable], + function (list, first) + local item, i, jof, bigtable; + # Simple checks + if IsEmpty(list) then + Error("Usage: Cannot take DirectProduct of zero items."); + elif Length(list) = 1 then + return list[1]; + fi; + # See if we can handle all objects + for i in [2..Length(list)] do + if not HasMultiplicationTable(list(i)) then + return DirectProductOp(Permuted(list, (1,i)), list[i]); + fi; + od; + # OK, safe to take everyone's multiplication table. + # So go ahead and make the big thing + bigtable := ProductTableOfCanonicalCayleyTables( + List(list, MultiplicationTable)); + # But we have to figure out what to do with it. + jof := RoughJoinOfFilters@(list, first); + # Dispatch modeled after the general one + if "IsMagmaWithOne" in jof then + if "IsMonoid" in jof then + return MonoidByMultiplicationTable(bigtable); + fi; + return MagmaWithOneByMultiplicationTable(bigtable); + fi; + if "IsAssociative" in jof then + return SemigroupByMultiplicationTable(bigtable); + elif "IsLeftQuasigroup" in jof then + if "IsLSelfDistributive" in jof then + if "IsElementwiseIdempotent" in jof then + return LeftQuandleByMultiplicationTableNC(bigtable); + fi; + return LeftRackByMultiplicationTableNC(bigtable); + fi; + return LeftQuandleByMultiplicationTableNC(bigtable); + elif "IsRightQuasigroup" in jof then + if "IsLSelfDistributive" in jof then + if "IsElementwiseIdempotent" in jof then + return RightQuandleByMultiplicationTableNC(bigtable); + fi; + return RightRackByMultiplicationTableNC(bigtable); + fi; + return RightQuandleByMultiplicationTableNC(bigtable); + fi; + return MagmaByMultiplicationTable(bigtable); +end); diff --git a/gap/structure.gi b/gap/structure.gi index 5f888a8..905813b 100644 --- a/gap/structure.gi +++ b/gap/structure.gi @@ -503,3 +503,120 @@ InstallMethod(Opposite, "for a right quandle", Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftQuandleNC) ); +# Direct Products +# We actually try to handle here the general cases that are not otherwise +# handled by the groups functions in GAP as a whole, or the LOOPS package + +# Helper for roughly creating the join of the filters of a bunch of objects. +FiltersOfObj@ := function(obj) + local f; + f := CategoriesOfObject(obj); + Append(f,KnownTruePropertiesOfObject(obj)); + Append(f,List(KnownAttributesOfObject(obj), x -> Concatenate("Has", x))); + return f; +end; + +# From the implementation of DirectProductOp for groups and quasigroups, the +# below will only be called with a second-argument collection which is not a +# group or quasigroup. + +# This is a helper that creates a rough join of the filters of a list of +# objects, using the same arguments as directproduct op (the redundant "first" +# argument, which is handy for seeding the filter list) +RoughJoinOfFilters@ := function(list, first); + local item, jof; + jof := Set(FiltersOfObj@(first)); + for item in list{[2..Length(list)]} do + IntersectSet(jof, FiltersOfObj@); + od; +end; + +InstallOtherMethod(DirectProductOp, "for a list and non-quasigroup magma", + [IsList, IsMagma], + function (list, first) + local item, i, jof, ids, gens, g, current, genfunc, genlists; + # Simple checks + if IsEmpty(list) then + Error("Usage: Cannot take DirectProduct of zero items."); + elif Length(list) = 1 then + return list[1]; + fi; + jof := RoughJoinOfFilters(list, first); + + # The dispatch below is somewhat ad-hoc, but covers the major existing cases + + # First, if we don't have generators for all of the items, we're kinda out + # of luck here: + if not ForAny(jof, nm -> StartsWith(nm,"HasGenerators")) then + TryNextMethod(); + fi; + # Next, if anything is not even a magma, recommend cartesian product + if not "IsMagma" in jof then + Info(InfoRAQ, 1, "Try Cartesian() for products of collections."); + TryNextMethod(); + fi; + + # The primary division now is between entities with identities, for which + # the product will have a smaller generating set, and those which do + # not, for which the generators is the full cartesian product of generators. + if "IsMagmaWithOne" in jof then + # Code basically copied from DirectProductOp() for general groups; + # would be better if this could be moved into the GAP core + ids := List(list, One); + gens := []; + for i in [1..Length(list)] do + for g in GeneratorsOfMagmaWithOne(list[i]) do + current := ShallowCopy(ids); + current[i] = g; + Add(gens, DirectProductElement(current)); + od; + od; + # Now, we want the most specific structure we know about here. + # Only aware of MagmaWithOne and Monoid, as direct products of + # loops are already covered by the LOOPS package: + if "IsMonoid" in jof then + return MonoidByGenerators(gens); + fi; + return MagmaWithOneByGenerators(gens); + fi; + # OK, here we know not all structures have inverses. Therefore, we must + # resort to the full cartesian product of generators. + # But we need to figure out what function to use to obtain the generators. + if "HasGeneratorsOfLeftQuasigroup" in jof then + genfunc := GeneratorsOfLeftQuasigroup; + elif "HasGeneratorsOfRightQuasigroup" in jof then + genfunc := GeneratorsOfRightQuasigroup; + elif "HasGeneratorsOfMagma" in jof then + genfunc := GeneratorsOfMagma; + else + Info(InfoRAQ,1, "RAQ: Unusual product, each of ", list, + " has generators, but not sure what kind; trying next method."); + TryNextMethod(); + fi; + + genlists := List(list, genfunc); + gens := Cartesian(genlists); + Apply(gens, DirectProductElement); + # Again, we need to figure out what sort of structure we might have + if "IsAssociative" in jof then + return SemigroupByGenerators(gens); + elif "IsLeftQuasigroup" in jof then + if "IsLSelfDistributive" in jof then + if "IsElementwiseIdempotent" in jof then + return LeftQuandleNC(gens); + fi; + return LeftRackNC(gens); + fi; + return LeftQuasigroupNC(gens); + elif "IsRightQuasigroup" in jof then + if "IsRSelfDistributive" in jof then + if "IsElementwiseIdempotent" in jof then + return RightQuandleNC(gens); + fi; + return RightRackNC(gens); + fi; + return RightQuasigroupNC(gens); + fi; + # Not seeing any additional structure; did I miss anything? + return MagmaByGenerators(gens); +end);