RightQuasigroups is a package for GAP that supports calculations with finite right quasigroups, quasigroups, loops and various varieties of right quasigroups, such as racks and quandles.
First time users should read Section 1.1, skim the rest of this chapter and look inside Chapter 2 for examples on how right quasigroups are constructed.
Most functions are intuitively named and can be traced in the manual from the index.
Sections and subsections marked by * are for users who wish to understand the inner workings of the package.
Representation
Right quasigroups, quasigroups and loops are represented as a subcategory of magmas in GAP. See Section 1.2 for more technical details and Section 1.3 for how right quasigroups and their elements are displayed.
Underlying set
Every right quasigroup has GAP elements and also has an underlying set. The underlying set is used to display GAP elements nicely and in Cayley tables. Furthermore, the underlying set plays an important role in right quasigroups that are not index based (see below) because the multiplication operation is then based on the underlying set. For index based right quasigroups, the underlying set is cosmetic and can be changed. See Section 1.4.
Arithmetic operations
Right quasigroups come equipped with the arithmetic operations of multiplication and right division. Quasigroups have additionally a left division operation, and loops have also a neutral element with respect to multiplication. See Section 1.5.
Index based and non-index based right quasigroups
Every right quasigroup is constructed either as an index based right quasigroup or as a right quasigroup that is not index based. Generally speaking, index based right quasigroups take longer to construct, cannot be very large (thousands of elements) and can be calculated with fast, while non-index based right quasigroups are constructed quickly, can be very large (millions of elements) but only basic methods will work for them. The fundamental piece of data for index based right quasigroups is the multiplication table, while for non-index based right quasigroups it is the multiplication function on the underlying set. See Section 1.8.
Most constructors accept an optional argument that determines whether the resulting right quasigroup will be index based and whether arguments will be checked. By default, the resulting right quasigroups will be index based and arguments will not be checked (!). See Chapter 2.
Parent
The parent mechanism is employed in GAP and in RightQuasigroups to save memory and to take advantage of the containment of subalgebras in their enveloping algebras. An element of a right quasigroup Q
knows into which parent right quasigroup it belongs and it therefore has access to all data stored in the parent quasigroup. See Sections 1.7 and 1.11.
Mappings
RightQuasigroups uses three kinds of mappings: GAP mappings, transformations, and permutations. Transformations that represent mappings between two right quasigroups, as well as permutations that represent bijective mappings on a right quasigroup, are indexed either with respect to the order of elements in the source and range quasigroups (so called canonical tranformations and canonical permutations) or with respect to their parents (so called parent tranformations and parent permutations). See Chapter 4.
The package works with parent permutations as much as possible, cf. Chapter 5. But canonical permutations are useful in the context of multiplication tables, and transformations are useful for left translations of right quasigroups, for instance.
Info class
The info class for the package is called InfoRightQuasigroups
and its initial value is set to 1, which will only print information on tasks that are assumed to take a long time to execute, such as reading and initializing a large data file. The user can prevent all messages from RightQuasigroups by calling SetInfoLevel( InfoRightQuasigroups, 0 )
. On the other hand, setting the info level higher for InfoRightQuasigroups
might result in additional messages.
Global variables
Finally, global variables and auxiliary functions in RightQuasigroups start with the prefix RQ_
and they are not fully documented in this manual. More information on these functions can be found in the declaration files gap\*.gd
.
Given a magma \((Q,\cdot)\) and \(x\in Q\), the right translation by \(x\) in \(Q\) is the mapping \(R_x:Q\to Q\) defined by \(R_x(y)=yx\), while the left translation by \(x\) in \(Q\) is the mapping \(L_x:Q\to Q\) defined by \(L_x(y)=xy\). The binary operation \(\cdot\) will be referred to as multiplication.
A magma \((Q,\cdot)\) is a right quasigroup if for every \(x\in Q\) the right translation \(R_x\) is a permutation of \(Q\). We then denote \(R_x^{-1}(y)\) by \(y/x\) and refer to \(/\) as right division.
Dually, a magma \((Q,\cdot)\) is a left quasigroup if for every \(x\in Q\) the left translation \(L_x\) is a permutation of \(Q\). We then denote \(L_x^{-1}(y)\) by \(x\backslash y\) and refer to \(\backslash\) as left division.
If \((Q,\cdot)\) is both a right quasigroup and a left quasigroup, it is a quasigroup.
A loop is a quasigroup \((Q,\cdot)\) with a neutral element \(e\in Q\) satisfying \(x\cdot e=x=e\cdot x\) for every \(x\in Q\).
In GAP, right quasigroups and their elements are constructs that mimic the mathematical objects. The set on which a right quasigroup is based is called the underlying set, cf. Section 1.3.
From a universal-algebraic point of view, right quasigroups, quasigroups and loops have different signatures, which is why there are separate, nested representations for the three kinds of algebras and their elements.
‣ IsRightQuasigroupElement ( object ) | ( filter ) |
‣ IsRightQuasigroupElmRep ( object ) | ( filter ) |
‣ IsRightQuasigroup ( object ) | ( filter ) |
Returns: true
or false
.
These are the GAP categories and representations for right quasigroup elements and right quasigroups. IsRightQuasigroupElement
is contained in the filter IsMultiplicativeElement
, IsRightQuasigroupElmRep
is contained in the filters IsPositionalObjectRep
and IsMultiplicativeElement
, and IsRightQuasigroup
is contained in the filter IsMagma
.
‣ IsQuasigroupElement ( object ) | ( filter ) |
‣ IsQuasigroupElmRep ( object ) | ( filter ) |
‣ IsQuasigroup ( object ) | ( filter ) |
Returns: true
or false
These are the GAP categories and representations for quasigroup elements and quasigroups. IsQuasigroupElement
is contained in the filter IsRightQuasigroupElement
, IsQuasigroupElmRep
is contained in the filters IsPositionalObjectRep
and IsMultiplicativeElement
, and IsQuasigroup
is contained in the filter IsRightQuasigroup
.
‣ IsLoopElement ( object ) | ( filter ) |
‣ IsLoopElmRep ( object ) | ( filter ) |
‣ IsLoop ( object ) | ( filter ) |
Returns: true
or false
These are the GAP categories and representations for loop elements and loops. IsLoopElement
is contained in the filters IsQuasigroupElement
and IsMultiplicativeElementWithInverse
, IsLoopElmRep
is contained in the filters IsPositionalObjectRep
and IsMultiplicativeElementWithInverse
, and IsLoop
is contained in the filters IsQuasigroup
and IsMultiplicativeElementWithInverseCollection
.
Note that these declarations do not imply that every loop element has an inverse.
‣ CategoryOfRightQuasigroup ( obj ) | ( operation ) |
Returns: If the argument is a right quasigroup, returns the smallest category from among IsRightQuasigroup
, IsQuasigroup
and IsLoop
into which the right quasigroup belongs. If the argument is a list of right quasigroups, returns the smallest category from among IsRightQuasigroup
, IsQuasigroup
and IsLoop
into which all the right quasigroups on the list belong.
All declared right quasigroups, quasigroups and loops belong to the filter IsRightQuasigroup
. It is often useful to know if a right quasigroup is in fact declared as a quasigroup or a loop, which is what the above method furnishes.
gap> Q := QuasigroupByCayleyTable( [[0,1],[1,0]] ); # declared quasigroup, in fact a group mathematically <quasigroup of size 2> gap> [ IsMagma( Q ), IsRightQuasigroup( Q ), IsQuasigroup( Q ), IsLoop( Q ), IsGroup( Q ) ]; [ true, true, true, false, false ] gap> CategoryOfRightQuasigroup( Q ); <Category "IsQuasigroup"> gap> CategoryOfRightQuasigroup( [ Q, ProjectionRightQuasigroup( 5 ) ] ); # common category <Category "IsRightQuasigroup">
The View
, Display
and Print
methods are implemented for right quasigroups as dynamic methods that display (some) currently known information about the object.
If Q
has a name (typically when Q
is a library object), View( Q )
prints Name( Q )
, e.g., <Moufang loop 64/12>
. In all other situations, View( Q )
reveals at least the size of Q
, as in <right quasigroup of size 8>
, <quasigroup of size 8>
or <loop of size 8>
, depending on whether Q
is declared as a right quasigroup, quasigroup or loop. Once additional properties of Q
become known, one of the strongest properties of Q
is also included in View( Q )
, e.g., <associative loop of order n>
.
Print(Q)
and Display( Q )
additionally displays up to the first 5 elements of the underlying set of Q
.
The String
attribute is also implemented for right quasigroups. Initially it behaves as View
, except that the returned value is returned as a string, not just displayed in the terminal window. Since String
is an attribute, the value is set at first call and does not change dynamically, unlike View
.
gap> Q := QuasigroupByCayleyTable( [[0,1],[1,0]] ); <quasigroup of size 2> gap> String( Q ); "<quasigroup of size 2>" gap> IsAssociative( Q ); true gap> Q; <associative quasigroup of size 2> gap> Display( Q ); <associative quasigroup of size 2 on 0, 1> gap> Print( Q ); <associative quasigroup of size 2 on 0, 1> gap> String( Q ); # was stored as attribute at first call "<quasigroup of size 2>"
The View
, Display
and Print
methods are implemented for right quasigroup elements.
By default, if x
is an element of a right quasigroup Q
and e
is the underlying element of x
, both View( x )
and Display( x )
display the character r
or q
or l
(depending on whether Q
is declared as a right quasigroup, a quasigroup or a loop), followed by the result of View( e )
. The method Print( x )
behaves similarly, except that it calls Print( e )
.
The String
attribute is also implemented for right quasigroup elements. It returns the same value as View
, except that the returned value is a string. Since right quasigroup elements are not attribute storing, the attribute String
is dynamic.
‣ SetRightQuasigroupElementsName ( Q, s ) | ( operation ) |
‣ SetQuasigroupElementsName ( Q, s ) | ( operation ) |
‣ SetLoopElementsName ( Q, s ) | ( operation ) |
Returns: true
.
Changes the name of all elements of the parent of Q for the purposes of displaying so that the name of every element starts with the prefix (string) s. Note that it is possible to change the prefix to an empty string, in which case the elements of the parent of Q will be displayed exactly as the elements of the underlying set; this improves legibility but might lead to confusion.
gap> Q := AsLoop( Group( (1,2) ) ); <associative loop of size 2> gap> String( Q.1 ); "l()" gap> Elements( Q ); [ l(), l(1,2) ] gap> SetLoopElementsName( Q, "g" );; Elements( Q ); [ g(), g(1,2) ] gap> String( Q.1 ); # dynamic since right quasigroup elements are not attribute storing "g()" gap> SetLoopElementsName( Q, "" );; Elements( Q ); # better legibility but perhaps confusing [ (), (1,2) ] gap> IsPerm( last[1] ); false
Every right quasigroup Q
consists of GAP elements returned via Elements( Q )
. In addition, every right quasigroup also has an underlying set accesible via UnderlyingSet( Q )
. The underlying set is used in displaying GAP elements of right quasigroups (cf. Section 1.3) and in Cayley tables (cf. Section 2.4).
If a right quasigroup is not index based (cf. Section 1.8), then the underlying set plays a critical role since its elements are used as arguments of the multiplication function. In non-index based right quasigroups the underlying set is merely cosmetic and can be changed at any time.
‣ UnderlyingSetElm ( obj ) | ( operation ) |
Returns: If obj is a right quasigroup element, returns the corresponding element of the underlying set. If obj is a list of right quasigroup elements (possibly from different right quasigroups), returns the list of the corresponding elements of the underlying set. If obj is a right quasigroup, returns the underlying set of obj.
‣ UnderlyingSet ( Q ) | ( operation ) |
Returns: the underlying set of the right quasigroup Q. Note that UnderlyingSet(
Q )
has the same effect as UnderlyingSetElm(
Q )
.
‣ ChangeUnderlyingSet ( Q, S ) | ( operation ) |
Returns: true
.
If Q is a right quasigroup that is index based and its own parent, ChangeUnderlyingSet(
Q,
S )
changes the underlying set of Q to S. If the Cayley table of Q was previously stored, it will be automtically recalculated. The argument S must be a collection and it is internally sorted.
Note that is it not possible to change the underlying set of a non-index based right quasigroup because the multiplication function then depends on the underlying set and it would have to be changed as well. In addition, we do not support changing the underlying set for a single element or for a list of elements because the underlying set of a parent quasigroup is sorted and the order might not be maintained by local changes.
gap> Q := AsLoop( SymmetricGroup( 3 ) );; gap> UnderlyingSetElm( Q.1 ); () gap> UnderlyingSet( Q ); [ (), (2,3), (1,2), (1,2,3), (1,3,2), (1,3) ] gap> ChangeUnderlyingSet( Q, ['a','b','c','d','e','f'] ); true gap> UnderlyingSet( Q ); "abcdef" gap> CayleyTable( Q ); [ "abcdef", "badcfe", "ceafbd", "dfbeac", "ecfadb", "fdebca" ]
The \(i\)th element of a right quasigroup Q
can be obtained by Elements( Q )[ i ]
.
The \(i\)th element of the parent Parent( Q )
or a right quasigroup Q
can be obtained by Q.i
(see Section 1.7). Note that Q.i
need not be the same element as Elements( Q )[ i ]
, in fact, it need not even be an element of Q
.
Finally, if x
is an element of the underlying set of Parent( Q )
, the corresponding element of Parent( Q )
is obtained by Q[x]
. Note that Q[x]
therefore need not be an element of Q
.
The product of two right quasigroup elements x
and y
is obtained by x*y
. The right quotient of x
and y
is obtained via x/y
, RightQuotient( x, y )
or RightDivision( x, y )
. In case of quasigroups, the left division is obtained via LeftQuotient( x, y )
or LeftDivision( x, y )
but not by x\y
since \
is not supported in GAP as a binary operation symbol.
For each of these operations, one of the two arguments can be a list of right quasigroup elements or a right quasigroup, in which case the corresponding list is returned. We allow x*Q
etc even if x
is an element of Parent(Q)
, not necessarily an element of Q
.
‣ RightQuotient ( x, y ) | ( operation ) |
Returns: the right quotient of right quasigroup elements x and y, that is, the unique element z
such that x = z*
y. The synonym RightDivision(
x,
y )
is also supported.
‣ LeftQuotient ( x, y ) | ( operation ) |
Returns: the left quotient of quasigroup elements x and y, that is, the unique element z
such that y =
x*z
. The synonym LeftDivision(
x,
y )
is also supported.
‣ One ( Q ) | ( attribute ) |
‣ RightInverse ( x ) | ( attribute ) |
‣ LeftInverse ( x ) | ( attribute ) |
In a loop Q
, the neutral element is returned by One( Q )
. Note that the neutral element need not be the first element Elements( Q )[ 1 ]
of Q
.
If e
is the neutral element of Q
, then for every x
in Q
there are y
and z
in Q
such that x*y = z*x = e
. The element y
is the right inverse of x
and is returned by RightInverse( x )
. Dually, the element z
is the left inverse of x
and is returned by LeftInverse( x )
.
If the two inverses of x
coincide, the two-sided inverse of x
is returned by Inverse(x)
or by x^-1
.
Products without specified parentheses are evalutated from left to right, i.e., x*y*z = (x*y)*z
.
A magma M
is said to be power associative if for every x
in M
the submagma generated by x
is a group. In particular, powers x^n
are well-defined in power associative magmas. Even if M
is not power-associative, x^n
with positive n
returns the element of M
obtained from the binary expansion of the exponent n
.
If M
is a magma with neutral element e
and x
is an element of M
, then Order( x )
returns the smallest nonnegative integer n
such that x^n=e
, if possible, else returns fail
. Note that in view of the above remarks, Order( x )
always returns a nonnegative integer if x
is an element of a finite loop.
The exponent Exponent( Q )
returns the smallest nonnegative integer n
such that x^n
is the neutral element of Q
, if such an integer exists, else it returns fail
. Note again that Exponent( Q )
will never fail for a finite loop Q
in RightQuasigroups, even if Q
is not power associative.
If n
is negative and x
has a two-sided inverse, then x^n
is calculated as (x^(-1))^(-n)
.
‣ Commutator ( x, y ) | ( operation ) |
Returns: the commutator of x
and y
, that is, the unique element z = (x*y)/(y*x)
satisfying x*y = z*(y*x)
. This is a logical choice for the elementwise commutator in right quasigroups.
Group-like commutators are obtained via Comm( x, y )
. If x
and y
are quasigroup elements, Comm( x, y )
returns the unique element z = LeftQuotient( y*x, x*y )
that satisfies x*y = (y*x)*z
. When the underlying quasigroup is a loop with two-sided inverses in which the antiautomorphic inverse property \((xy)^{-1}=y^{-1}x^{-1}\) and the left inverse property \(x^{-1}(xy) = y\) hold, then Comm( x, y )
coincides with the GAP commutator x^(-1)*y^(-1)*x*y
.
‣ Associator ( x, y, z ) | ( operation ) |
Returns: the associator of x
, y
and z
, that is, the unique element u = (x*(y*z))/((x*y)*z)
satisfying x*(y*z) = u*((x*y)*z)
.
Arithmetic operations in right quasigroups (quasigroup, loops) are carried out either via tables or via functions, depending on whether the algebra in question is index based or not. See Section 1.8 for more information on index based versus non-index based right quasigroups, Section 2.4 for details on multiplication tables and Cayley tables, and Section 2.5 for details on how functions can be used as arithmetic operations.
Chapter 2 contains a comprehensive list of right quasigroup constructors. Here we present two examples, starting with a right quasigroup constructor based on a multiplication function.
gap> Q := RightQuasigroupByFunction( [0..3], function( x,y ) return (x+2*y) mod 4; end ); # index based by default <right quasigroup of size 4> gap> UnderlyingSet( Q ); [ 0, 1, 2, 3 ] gap> Elements( Q ); # default prefix "r" is assigned to right quasigroup elements [ r0, r1, r2, r3 ] gap> [ Elements(Q)[1], Q.1, Q[0] ]; # three ways of accessing elements [ r0, r0, r0 ] gap> Display( CayleyTable( Q ) ); # based on the underlying set [ [ 0, 2, 0, 2 ], [ 1, 3, 1, 3 ], [ 2, 0, 2, 0 ], [ 3, 1, 3, 1 ] ] gap> Display( MultiplicationTable( Q ) ); # based on [1..n], here [1..4] [ [ 1, 3, 1, 3 ], [ 2, 4, 2, 4 ], [ 3, 1, 3, 1 ], [ 4, 2, 4, 2 ] ] gap> mult := MultiplicationFunction( Q ); # based on [1..n] since Q is index based function( i, j ) ... end gap> mult( 1, 1 ); 1 gap> Q[0]*Q[1]; r2 gap> Q[0]/Q[1]; # RightQuotient and RightDivision are also supported r2
If, as in the above example, the remaining operations (right division, left division, neutral element) are not provided by the user, they are automatically inferred from the given multiplication function depending on the type of algebra under construction. This might (and typically will) lead to slower division operations in the non-index based case. For instance, if only the multiplication function for a right quasigroup Q
is given and Q
is not index based, then the right quotient x/y
is obtained by locating the first (and only) element z
of Q
such that x = z*y
; this is slow when Q
is large.
Here is an example of a loop constructor based on a conversion from a group.
gap> Q := AsLoop( SymmetricGroup( 3 ), ConstructorStyle( false, false ) ); # not index based, arguments not checked <associative loop of size 6> gap> UnderlyingSet( Q ); [ (), (2,3), (1,2), (1,2,3), (1,3,2), (1,3) ] gap> Elements( Q ); # default prefix "l" is assigned to loop elements [ l(), l(2,3), l(1,2), l(1,2,3), l(1,3,2), l(1,3) ] gap> mult := MultiplicationFunction( Q ); # based on the underlying set since Q is not index based <Operation "*"> gap> mult( (1,2), (1,3) ); (1,2,3) gap> One( Q ); l() gap> Commutator( Q[(1,2)], Q[(1,3)] ); l(1,3,2) gap> Associator( Q[(1,2)], Q[(1,3)], Q[(2,3)] ); l()
Note that right quasigroups that happen to be quasigroups and/or loops mathematically must be explicitly declared as such in GAP to make quasigroup and/or loop methods available to them. For instance, the above loop of size 6 is an associative loop (that is, a group), but it will not be automatically recognized as a group by GAP. There are methods provided that check if a given right quasigroup is mathematically a quasigroup or a loop, cf. Section 2.2.
The parent mechanism is employed in GAP and in RightQuasigroups to save memory and to take advantage of the containment of subalgebras in the enveloping algebras. The parent Parent( Q )
of a right quasigorup Q
is the largest right quasigroup from which Q
has been constructed as a subalgebra. In more detail, if Q
is constructed as a subalgebra of a right quasigroup P
then Parent( Q ) = Parent( P )
, while if Q
is not constructed as a subalgebra of some right quasigroup then Parent( Q ) = Q
.
Right quasigroup elements are created automatically every time a new right quasigroup is constructed, with one exception: When Q
is constructed as a subalgebra then the elements of Q
are inherited from Parent( Q )
. In particular, if x
is any element of Q
then F = FamilyObj( x )
points to Parent( Q )
and many attributes of Parent( Q )
can be accessed via F
(see Section 1.11).
gap> Q := RightQuasigroupByFunction([0..5], function(x,y) return (x+y) mod 6; end );; Elements( Q ); [ r0, r1, r2, r3, r4, r5 ] gap> A := Subrightquasigroup( Q, [2] ); <right quasigroup of size 3> gap> Elements( A ); [ r0, r2, r4 ] gap> Parent( A ) = Q; true gap> Elements( A )[ 3 ]; # the 3rd element of A r4 gap> A.3; # the 3rd element of the parent of A r2 gap> A[4]; # the element of the parent of A corresponding to the given underlying element r4 gap> Display( CayleyTable( A ) ); [ [ 0, 2, 4 ], [ 2, 4, 0 ], [ 4, 0, 2 ] ] gap> Display( MultiplicationTable( A ) ); [ [ 1, 2, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ] ]
Every right quasigroup Q
is constructed either as an index based right quasigroup or as a right quasigroup that is not index based.
Generally speaking, index based right quasigroups take longer to construct, cannot be very large (thousands of elements) and can be calculated with fast, while non-index based right quasigroups are constructed quickly, can be very large (millions of elements) but only basic methods will work for them.
If Q
is an index based right quasigroup of size \(n\), then the multiplication and divisions in Q
are carried out via multiplication and division tables. The multiplication and division functions might then be present as functions \([1..n]\times[1..n]\to[1..n]\) but they are not directly involved in carrying out arithmetic operations.
If Q
is a non-index based right quasigroup on the underlying set \(S\), then the multiplication and divisions in Q
are carried out via multiplication and division functions \(S\times S\to S\). The multiplication and division tables might then be present but they are not directly involved in carrying out arithmetic operations.
In more details, if Q
is a right quasigroup with parent P
of size \(n\), x
is an an element of Q
and F=FamilyObj( x )
, then:
If Q
is index based:
x![1]
is the position of x
among the elements of P
, i.e., the index of x
(see below),
the multiplication table of P
is precalculated and stored as F!.multTable (the attribute MultiplicationTable( Q )
is set when requested for the first time),
the division tables of P
are calculated and stored at first usage of the respective divisions,
the multiplication and division functions of P
, if present, are functions \([1..n]\times[1..n]\to [1..n]\) but all arithmetic operations are handled via tables,
the fundamental piece of data is F!.multTable
, the multiplication table of P
.
If Q
is not index based:
x![1]
is the element of the underlying set \(S\) of Q
corresponding to x
,
the mutliplication and division tables of P
are not precalculated,
the multiplication and division functions of P
are possibly slow functions \(S\times S\to S\), often based on some additional data provided by the user in the constructor,
the fundamental piece of data is F!.mult
, the multiplication function of P
.
A right quasigroup of size \(n\) is said to be canonical if it is index based, is its own parent and the underlying set is \([1..n]\). Many computationally intensive methods of RightQuasigroups internally work with canonical right quasigroups.
‣ ParentInd ( arg ) | ( attribute ) |
Returns: the index of the object arg. If arg is a right quasigroup element, it returns the index of arg, that is, the position of arg among the elements of the parent right quasigroup. If the argument is a list of right quasigroup elements, it returns the corresponding list of indices. If the argument is a right quasigroup, it returns the corresponding list of indices and stores the result as an attribute. Finally, if arg is a right quasigroup mapping with source Q1
and range Q2
, the function calls AsParentPerm(
arg )
if Q1 = Q2
and arg is bijective, else it calls AsParentTransformation(
arg )
.
‣ IsIndexBased ( Q ) | ( operation ) |
Returns: true
if the right quasigroup Q is index based, else returns false
.
‣ IndexBasedCopy ( Q ) | ( operation ) |
Returns: a copy of Q that has the same underlying set as Q, is index based and is its own parent. An effort is made to inherit properties from Q.
Note that there is no general method available for converting index based right quasigroups into right quasigroups that are not index based.
‣ IsCanonical ( Q ) | ( operation ) |
Returns: true
if the right quasigroup Q of size \(n\) is canonical, else returns false
.
‣ CanonicalCopy ( Q ) | ( operation ) |
Returns: a canonical copy of the right quasigroup Q. An effort is made to inherit properties from Q.
Note that there is no general method available for converting canonical right quasigroups into right quasigroups that are not canonical.
The following example illustrates basic features of non-index based right quasigroups.
gap> Q := RightQuasigroupByFunction( GF( 9 ), \+, ConstructorStyle( false, false ) ); # not index based, arguments not checked <right quasigroup of size 9> gap> IsIndexBased( Q ); false gap> x := Q.2; rZ(3)^0 gap> x![1]; # the underlying element Z(3)^0 gap> x*x; rZ(3) gap> F := FamilyObj( x ); <Family: "RightQuasigroupFam"> gap> [ IsBound( F!.mult ), IsBound( F!.rdiv ), IsBound( F!.ldiv ) ]; # no left division in a right quasigroup [ true, true, false ] gap> [ IsBound( F!.multTable ), IsBound( F!.rdivTable ), IsBound( F!.ldivTable ) ]; # no tables are bound [ false, false, false ] gap> mult := MultiplicationFunction( Q ); <Operation "+"> gap> mult( Z(3), Z(3) ); Z(3)^0 gap> rdiv := RightDivisionFunction( Q ); # the constructor does not know that this really is <Operation "-"> function( x, y ) ... end gap> rdiv( Z(3), Z(3) ); 0*Z(3)
Note how things change in index based and canonical copies.
gap> Q := RightQuasigroupByFunction( GF( 9 ), \+, ConstructorStyle( false, false ) );; # same as in the above example, not index based gap> R := IndexBasedCopy( Q );; gap> IsIndexBased( R ); true gap> UnderlyingSet( R ); # no change to the underlying set [ 0*Z(3), Z(3)^0, Z(3), Z(3^2), Z(3^2)^2, Z(3^2)^3, Z(3^2)^5, Z(3^2)^6, Z(3^2)^7 ] gap> IsCanonical( R ); # underlying set is not [1..n] false gap> x := R.2;; gap> x![1]; # the index of x in the parent of R (here R itself) 2 gap> x*x; rZ(3) gap> F := FamilyObj( R.1 );; gap> [ IsBound( F!.mult ), IsBound( F!.rdiv ), IsBound( F!.ldiv ) ]; # if bound then based on respective tables [ true, true, false ] gap> [ IsBound( F!.multTable ), IsBound( F!.rdivTable ), IsBound( F!.ldivTable ) ]; # division tables will be bound when divisions are called [ true, false, false ] gap> x/x; r0*Z(3) gap> IsBound( F!.rdivTable ); true gap> mult := MultiplicationFunction( R ); # the multiplication function is based on indices function( i, j ) ... end gap> mult( 1, 1 ); 1 gap> C := CanonicalCopy( Q ); <right quasigroup of size 9> gap> UnderlyingSet( C ); # underlying set has changed to [1..n] [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
GeneratorsOfRightQuasigroup
, GeneratorsOfQuasigroup
and GeneratorsOfLoop
are supported as synonyms of GeneratorsOfMagma
.
‣ SmallGeneratingSet ( Q ) | ( attribute ) |
Returns: a small generating set of a right quasigroup Q obtained by a greedy algorithm that starts with the empty set of generators and in every steps adds the first element of Q that enlarges the generated subalgebra the most. There is no guarantee that SmallGeneratingSet
will return a generating set of smallest possible cardinality. If the returned set gens
of generators is smaller in cardinality than GeneratorsOfRightQuasigroup(
Q )
, the value of GeneratorsOfRightQuasigroup(
Q )
is automatically set to gens
.
‣ GeneratorsSmallest ( Q ) | ( attribute ) |
Returns: the smallest generating set of Q with respect to the lexicographic ordering based on the linear ordering of elements of Q. If the returned set gens
of generators is smaller in cardinality than GeneratorsOfRightQuasigroup(
Q )
, the value of GeneratorsOfRightQuasigroup(
Q )
is automatically set to gens
.
If A
and B
are right quasigroups with the same parent, cf. Section 1.7, then A<B
iff GeneratorsSmallest( A )<GeneratorsSmallest( B )
. When two right quasigroups do not have the same parent, they cannot be compared.
There is a fast test for equality of two right quasigroups A
, B
that avoids calling GeneratorsSmallest
.
Just like with other GAP objects, if A = B
returns true
, this does not imply that A
and B
are identical GAP objects, merely that they have the same parent and consist of the same elements. In particular, if A = Parent( A )
holds then A
is not necessarily its own parent; it might be a subalgebra of Parent( A )
that happens to contain all elements of Parent( A )
. (One can call IsIdenticalObj( A, Parent( A ) )
to check whether A
truly is its own parent.)
gap> Q := AsLoop( GF(8) );; gap> GeneratorsOfLoop( Q ); # trivial generating set [ l0*Z(2), lZ(2)^0, lZ(2^3), lZ(2^3)^2, lZ(2^3)^3, lZ(2^3)^4, lZ(2^3)^5, lZ(2^3)^6 ] gap> GeneratorsSmallest( Q ); # with respect to lexicographic ordering [ lZ(2^3)^4, lZ(2^3)^5, lZ(2^3)^6 ] gap> GeneratorsOfLoop( Q ); # changed since a smaller generating set has been found [ lZ(2^3)^4, lZ(2^3)^5, lZ(2^3)^6 ] gap> SmallGeneratingSet( Q ); # with respect to greedy algorithm [ lZ(2)^0, lZ(2^3), lZ(2^3)^2 ] gap> GeneratorsOfLoop( Q ); # not changed since no smaller generating set has been found [ lZ(2^3)^4, lZ(2^3)^5, lZ(2^3)^6 ]
There are several so-called non-qualified operations in GAP. These are operations which are not attributes or properties but whose result depends on the type of the argument. As far as RightQuasigroups is concerned, the non-qualified operations are DerivedSeries
, IsNilpotent
, IsSimple
, IsSolvable
, LowerCentralSeries
and UpperCentralSeries
. For all such operations we provide qualified methods, e.g., DerivedSeriesOfLoop( Q )
called with a loop Q
as argument, but also support the non-qualified versions, e.g., DerivedSeries( Q )
.
Let Q
be a right quasigroup, F
the family object of any element of Q
(that is, F = FamilyObj(Q.1)
) and P
the parent of Q
(see Section 1.7). Then:
F!.cayleyTable
is the Cayley table of P
.
F!.indexBased
determines is P
and hence also Q
are index based.
F!.ldiv
is the left division function of P
, if Q
is a declared quasigroup.
F!.ldivTable
is the left division table of P
, if Q
is a declared quasigroup.
F!.mult
is the multiplication function of P
.
F!.multTable
is the multiplication table of P
.
F!.names
is the prefix used for all elements of P
.
F!.one
is the neutral element of P
and hence also of Q
, if Q
is a declared loop.
F!.parent
is P
.
F!.rdiv
is the right division function of P
.
F!.rdivTable
is the right division table of P
.
F!.rSection
is the right section for P
.
F!.set
is the set of GAP elements of P
.
F!.size
is the size of P
.
F!.uSet
is the underlying set of P
. Note that if Q
is a proper subalgebra of P
then UnderlyingSet( Q )
is a proper subset of F!.uSet
.
Note that not all of the above components of F
are necessarily bound, depending on the constructor used for P
. To see the list of bound components, call NamesOfComponents( F )
.
generated by GAPDoc2HTML