<Chapter Label=
"Funct">
<Heading>&Circle; functions</Heading>
To use the &Circle; package first you need to load it as follows:
<Log>
<![
CDATA[
gap> LoadPackage(
"circle");
-----------------------------------------------------------------------------
Loading Circle 1.6.5 (Adjoint groups of finite rings)
by Olexandr Konovalov (
https://alex-konovalov.github.io/) and
Panagiotis Soules (psoules@math.uoa.gr).
maintained by:
Olexandr Konovalov (
https://alex-konovalov.github.io/).
Homepage:
https://gap-packages.github.io/circle
Report issues at
https://github.com/gap-packages/circle/issues
-----------------------------------------------------------------------------
true
gap>
]]>
</Log>
Note that if you entered examples from the previous chapter, you need
to restart &GAP; before loading the &Circle; package.
<Section Label=
"CircleObjects">
<Heading>Circle objects</Heading>
Because for elements of the ring <M>R</M> the ordinary multiplication
is already denoted by <C>*</C>, for the implementation of the circle
multiplication in the adjoint semigroup we need to wrap up ring
elements as CircleObjects, for which <C>*</C> is defined to be the
circle multiplication.
<ManSection>
<Attr Name=
"CircleObject"
Arg=
"x" />
<Description>
Let <A>x</A> be a ring
element. Then <C>CircleObject(x)</C> returns the
corresponding circle object. If <A>x</A> lies in the family <C>fam</C>,
then <C>CircleObject(x)</C> lies in the family <Ref Fam=
"CircleFamily"/>,
corresponding to the family <C>fam</C>.
<Example>
<![
CDATA[
gap> a := CircleObject( 2 );
CircleObject( 2 )
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Attr Name=
"UnderlyingRingElement"
Arg=
"x" />
<Description>
Returns the corresponding ring
element for the circle object <A>x</A>.
<Example>
<![
CDATA[
gap> a := CircleObject( 2 );
CircleObject( 2 )
gap> UnderlyingRingElement( a );
2
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Filt Name=
"IsCircleObject"
Arg=
"x"
Type=
"Category" />
<Filt Name=
"IsCircleObjectCollection"
Arg=
"x"
Type=
"Category" />
<Description>
An object <A>x</A> lies in the category <C>IsCircleObject</C> if and
only if it lies in a family constructed by <Ref Attr=
"CircleFamily"/>.
Since circle objects can be multiplied via <C>*</C> with elements in
their family, and we need operations <C>One</C> and <C>Inverse</C> to
deal with groups they generate, circle objects are implemented in the
category <C>IsMultiplicativeElementWithInverse</C>. A collection of
circle objects (e.g. adjoint semigroup or adjoint group) will lie in
the category <C>IsCircleObjectCollection</C>.
<Example>
<![
CDATA[
gap> IsCircleObject( 2 ); IsCircleObject( CircleObject( 2 ) );
false
true
gap> IsMultiplicativeElementWithInverse( CircleObject( 2 ) );
true
gap> IsCircleObjectCollection( [ CircleObject(0), CircleObject(2) ] );
true
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Filt Name=
"IsPositionalObjectOneSlotRep"
Arg=
"x"
Type=
"Representation" />
<Filt Name=
"IsDefaultCircleObject"
Arg=
"x"
Type=
"Representation" />
<Description>
To store the corresponding circle object, we need only to store the
underlying ring
element. Since this is quite common situation, we
defined the representation <C>IsPositionalObjectOneSlotRep</C> for a
more general case. Then we defined <C>IsDefaultCircleObject</C> as a
synonym of <C>IsPositionalObjectOneSlotRep</C> for objects in
<Ref Filt=
"IsCircleObject" />.
<Example>
<![
CDATA[
gap> IsPositionalObjectOneSlotRep( CircleObject( 2 ) );
true
gap> IsDefaultCircleObject( CircleObject( 2 ) );
true
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Attr Name=
"CircleFamily"
Arg=
"fam" />
<Description>
<C>CircleFamily(fam)</C> is a family, elements of which are in one-to-one
correspondence with elements of the family <A>fam</A>,
but with the circle multiplication as an infix multiplication.
That is, for <M>x</M>, <M>y</M> in <A>fam</A>, the product of their images
in the <C>CircleFamily(fam)</C> will be the image of <M> x + y + x y </M>.
The relation between these families is demonstrated by the following equality:
<Example>
<![
CDATA[
gap> FamilyObj( CircleObject ( 2 ) ) = CircleFamily( FamilyObj( 2 ) );
true
]]>
</Example>
</Description>
</ManSection>
</Section>
<Section Label=
"CircleOperations">
<Heading>Operations with circle objects</Heading>
<ManSection>
<Oper Name=
"One"
Arg=
"x" />
<Description>
This operation returns the multiplicative neutral
element
for the circle object <A>x</A>. The result is the circle
object corresponding to the additive neutral
element of
the appropriate ring.
<Example>
<![
CDATA[
gap> One( CircleObject( 5 ) );
CircleObject( 0 )
gap> One( CircleObject( 5 ) ) = CircleObject( Zero( 5 ) );
true
gap> One( CircleObject( [ [ 1, 1 ],[ 0, 1 ] ] ) );
CircleObject( [ [ 0, 0 ], [ 0, 0 ] ] )
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Oper Name=
"InverseOp"
Arg=
"x" />
<Description>
For a circle object <A>x</A>, returns the multiplicative
inverse of <A>x</A> with respect to the circle multiplication;
if such one does not exist then <K>fail</K> is returned.<P/>
In our implementation we assume that the underlying ring is a
subring of the ring with one, thus, if the circle inverse for
an
element <M>x</M> exists, than it can be computed as <M>-x(1+x)^{-1}</M>.
<Example>
<![
CDATA[
gap> CircleObject( -2 )^-1;
CircleObject( -2 )
gap> CircleObject( 2 )^-1;
CircleObject( -2/3 )
gap> CircleObject( -2 )*CircleObject( -2 )^-1;
CircleObject( 0 )
]]>
</Example>
<Example>
<![
CDATA[
gap> m := CircleObject( [ [ 1, 1 ], [ 0, 1 ] ] );
CircleObject( [ [ 1, 1 ], [ 0, 1 ] ] )
gap> m^-1;
CircleObject( [ [ -1/2, -1/4 ], [ 0, -1/2 ] ] )
gap> m * m^-1;
CircleObject( [ [ 0, 0 ], [ 0, 0 ] ] )
gap> CircleObject( [ [ 0, 1 ], [ 1, 0 ] ] )^-1;
fail
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Oper Name=
"IsUnit"
Arg=
"[ R, ] x" />
<Description>
Let <A>x</A> be a circle object corresponding to an
element of
the ring <A>R</A>. Then the operation <C>IsUnit</C> returns
<C>true</C>, if <A>x</A> is invertible in <A>R</A> with respect
to the circle multiplication, and <C>false</C> otherwise.
<Example>
<![
CDATA[
gap> IsUnit( Integers, CircleObject( -2 ) );
true
gap> IsUnit( Integers, CircleObject( 2 ) );
false
gap> IsUnit( Rationals, CircleObject( 2 ) );
true
gap> IsUnit( ZmodnZ(8), CircleObject( ZmodnZObj(2,8) ) );
true
gap> m := CircleObject( [ [ 1, 1 ],[ 0, 1 ] ] );;
gap> IsUnit( FullMatrixAlgebra( Rationals, 2 ), m );
true
]]>
</Example>
If the first argument is omitted, the result will be returned with respect
to the default ring of the circle object <A>x</A>.
<Example>
<![
CDATA[
gap> IsUnit( CircleObject( -2 ) );
true
gap> IsUnit( CircleObject( 2 ) );
false
gap> IsUnit( CircleObject( ZmodnZObj(2,8) ) );
true
gap> IsUnit( CircleObject( [ [ 1, 1 ],[ 0, 1 ] ] ) );
false
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Oper Name=
"IsCircleUnit"
Arg=
"[ R, ] x" />
<Description>
Let <A>x</A> be an
element of the ring <A>R</A>. Then
<C>IsCircleUnit( R, x )</C> determines whether <A>x</A> is invertible
in <A>R</A> with respect to the circle multilpication. This is
equivalent to the condition that 1+<A>x</A> is a unit in <A>R</A>
with respect to the ordinary multiplication.
<Example>
<![
CDATA[
gap> IsCircleUnit( Integers, -2 );
true
gap> IsCircleUnit( Integers, 2 );
false
gap> IsCircleUnit( Rationals, 2 );
true
gap> IsCircleUnit( ZmodnZ(8), ZmodnZObj(2,8) );
true
gap> m := [ [ 1, 1 ],[ 0, 1 ] ];
[ [ 1, 1 ], [ 0, 1 ] ]
gap> IsCircleUnit( FullMatrixAlgebra(Rationals,2), m );
true
]]>
</Example>
If the first argument is omitted, the result will be returned with respect
to the default ring of <A>x</A>.
<Example>
<![
CDATA[
gap> IsCircleUnit( -2 );
true
gap> IsCircleUnit( 2 );
false
gap> IsCircleUnit( ZmodnZObj(2,8) );
true
gap> IsCircleUnit( [ [ 1, 1 ],[ 0, 1 ] ] );
false
]]>
</Example>
</Description>
</ManSection>
</Section>
<Section Label=
"CircleAdjointGroups">
<Heading>Construction of the adjoint semigroup and adjoint group</Heading>
<ManSection>
<Attr Name=
"AdjointSemigroup"
Arg=
"R" />
<Description>
If <A>R</A> is a finite ring then <C>AdjointSemigroup(<A>R</A>)</C> will
return the monoid which is formed by all elements of <A>R</A> with
respect to the circle multiplication.
<P/>
The implementation is rather straightforward and was added to provide a
link to the &GAP; functionality for semigroups. It assumes that the
enumaration of all elements of the ring <A>R</A> is feasible.
<Example>
<![
CDATA[
gap> R:=Ring( [ ZmodnZObj(2,8) ] );;
gap> S:=AdjointSemigroup(R);
<monoid with 4 generators>
]]>
</Example>
</Description>
</ManSection>
<ManSection>
<Attr Name=
"AdjointGroup"
Arg=
"R" />
<Description>
If <A>R</A> is a finite radical algebra then
<C>AdjointGroup(<A>R</A>)</C> will return the adjoint group
of <A>R</A>, given as a group generated by a set of circle objects.
<P/>
To compute the adjoint group of a finite radical algebra,
&Circle; uses the fact that all elements of a radical algebra
form a group with respect to the circle multiplication. Thus,
the adjoint group of <A>R</A> coincides with <A>R</A> elementwise,
and we can randomly select an appropriate set of generators for
the adjoint group.
<P/>
The warning is displayed by <C>IsGeneratorsOfMagmaWithInverses</C>
method defined in <File>gap4r4/lib/grp.gi</File> and may be ignored.
<P/>
<B>WARNINGS:</B>
<P/>
1. The set of generators of the returned group is not required
to be a generating set of minimal possible order.
<P/>
2. <C>AdjointGroup</C> is stored as an attribute of <A>R</A>,
so for the same copy of <A>R</A> calling it again you will get
the same result. But if you will create another copy of <A>R</A>
in the future, the output may differ because of the random
selection of generators. If you want to have the same generating
set, next time you should construct a group immediately specifying
circle objects that generate it.
<P/>
3. In most cases, to investigate some properties of the adjoint
group, it is necessary first to convert it to an isomorphic
permutation group or to a PcGroup.
<P/>
For example, we can create the following commutative 2-dimensional
radical algebra of order 4 over the field of two elements,
and show that its adjoint group is a cyclic group of order 4:
<Example>
<![
CDATA[
gap> x:=[ [ 0, 1, 0 ],
> [ 0, 0, 1 ],
> [ 0, 0, 0 ] ];;
gap> R := Algebra( GF(2), [ One(GF(2))*x ] );;
gap> RadicalOfAlgebra( R ) = R;
true
gap> Dimension(R);
2
gap> G := AdjointGroup( R );;
gap> Size( R ) = Size( G );
true
gap> StructureDescription( G );
"C4"
]]>
</Example>
In the following example we construct a non-commutative
3-dimensional radical algebra of order 8 over the field
of two elements, and demonstrate that its adjoint group
is the dihedral group of order 8:
<Alt Only=
"LaTeX">\pagebreak</Alt>
<Example>
<![
CDATA[
gap> x:=[ [ 0, 1, 0 ],
> [ 0, 0, 0 ],
> [ 0, 0, 0 ] ];;
gap> y:=[ [ 0, 0, 0 ],
> [ 0, 0, 1 ],
> [ 0, 0, 0 ] ];;
gap> R := Algebra( GF(2), One(GF(2))*[x,y] );
<algebra over GF(2), with 2 generators>
gap> RadicalOfAlgebra(R) = R;
true
gap> Dimension(R);
3
gap> G := AdjointGroup( R );
<group of size 8 with 2 generators>
gap> StructureDescription( G );
"D8"
]]>
</Example>
If the ring <A>R</A> is not a radical algebra,
then &Circle; will use another approach. We will enumerate
all elements of the ring <A>R</A> and select those that are units with
respect to the circle multiplication.
Then we will use a random approach similar to the case of the radical
algebra, to find some generating set of the adjoint group. Again, all
warnings 1-3 above refer also to this case.
<P/>
Of course,
enumeration of all elements of <A>R</A> should be feasible
for this computation.
In the following example we demonstrate how it works for rings,
generated by residue classes:
<Example>
<![
CDATA[
gap> R := Ring( [ ZmodnZObj(2,8) ] );;
gap> G := AdjointGroup( R );
<group of size 4 with 2 generators>
gap> StructureDescription( G );
"C2 x C2"
gap> R := Ring( [ ZmodnZObj(2,256) ] );;
gap> G := AdjointGroup( R );;
gap> StructureDescription( G );
"C64 x C2"
]]>
</Example>
Due to the <Ref Attr=
"AdjointSemigroup" />, there is also another way to
compute the adjoint group of a ring <M>R</M> by means of the computation of its
adjoint semigroup <M>S(R)</M> and taking the Green
's <M>H</M>-class of the
multiplicative neutral
element of <M>S(R)</M>. Let us repeat the last example
in this way:
<Example>
<![
CDATA[
gap> R := Ring( [ ZmodnZObj(2,256) ] );;
gap> S := AdjointSemigroup( R );
<monoid with 128 generators>
gap> H := GreensHClassOfElement(S,One(S));
<Green
's H-class: <object>>
gap> G:=AsGroup(H);
<group of size 128 with 2 generators>
gap> StructureDescription(G);
"C64 x C2"
]]>
</Example>
However, the conversion of the Green
's <M>H</M>-class to the group may take
some time which may vary dependently on the particular ring in question, and
will also
display a lot of warnings about the default
<C>IsGeneratorsOfMagmaWithInverses</C> method, so we did not implemented
this as as standard method. In the following example the method based on
Green
's <M>H</M>-class is much slower than an application of
earlier described random approach (20s vs 10ms):
<Example>
<![
CDATA[
gap> R := Ring( [ ZmodnZObj(2,256) ] );;
gap> AdjointGroup(R);;
gap> R := Ring( [ ZmodnZObj(2,256) ] );;
gap> S:=AdjointSemigroup(R);
<monoid with 128 generators>
gap> AsGroup(GreensHClassOfElement(S,One(S)));
<group of size 128 with 2 generators>
]]>
</Example>
Finally, note that if <A>R</A> has a unity <M>1</M>, then the set <M>1+R^{ad}</M>,
where <M>R^{ad}</M> is the adjoint semigroup of <A>R</A>, coincides
with the multiplicative semigroup <M>R^{mult}</M> of <M>R</M>, and the map
<M> r \mapsto (1+r) </M> for <M>r</M> in <M>R</M> is an isomorphism
from <M>R^{ad}</M> onto <M>R^{mult}</M>.
<P/>
Similarly, the set <M>1+R^*</M>, where <M>R^{*}</M> is the adjoint
group of <A>R</A>, coincides with the unit group of <M>R</M>,
which we denote <M>U(R)</M>, and the map <M>r \mapsto (1+r)</M>
for <M>r</M> in <M>R</M> is an isomorphism from <M>R^*</M> onto
<M>U(R)</M>.
<P/>
We demonstrate this isomorphism using the following example.
<Example>
<![
CDATA[
gap> LoadPackage(
"laguna", false );
true
gap> FG := GroupRing( GF(2), DihedralGroup(8) );
<algebra-with-one over GF(2), with 3 generators>
gap> R := AugmentationIdeal( FG );;
gap> G := AdjointGroup( R );;
gap> IdGroup( G );
[ 128, 170 ]
gap> IdGroup( Units( FG ) );
#I LAGUNA package: Computing the unit group ...
[ 128, 170 ]
]]>
</Example>
Thus, dependently on the ring <C>R</C> in question, it might be possible
that you can compute much faster its unit group using <C>Units(R)</C> than
its adjoint group using <C>AdjointGroup(R)</C>. This is why in an attempt
of computation of the adjoint group of the ring with one a warning message
will be displayed:
<P/>
<Example>
<![
CDATA[
gap> Size( AdjointGroup( GroupRing( GF(2), DihedralGroup(8) ) ) );
WARNING: usage of AdjointGroup for associative ring <R> with one!!!
In this case the adjoint group is isomorphic to the unit group
Units(<R>), which possibly may be computed faster!!!
128
gap> Size( AdjointGroup( Integers mod 11 ) );
WARNING: usage of AdjointGroup for associative ring <R> with one!!!
In this case the adjoint group is isomorphic to the unit group
Units(<R>), which possibly may be computed faster!!!
10
]]>
</Example>
If <A>R</A> is infinite, an error message will appear,
telling that &Circle; does not provide methods to deal
with infinite rings.
</Description>
</ManSection>
</Section>
<!-- ######################################################### -->
<Section Label=
"Service">
<Heading>Service functions</Heading>
<ManSection>
<InfoClass Name=
"InfoCircle" />
<Description>
<C>InfoCircle</C> is a special Info class for &Circle; algorithms.
It has 2 levels: 0 (default) and 1. To change info level to
<C>k</C>, use command <C>SetInfoLevel(InfoCircle, k)</C>.
<Example>
<![
CDATA[
gap> SetInfoLevel( InfoCircle, 1 );
gap> SetInfoLevel(InfoCircle,1);
gap> R := Ring( [ ZmodnZObj(2,8) ]);;
gap> G := AdjointGroup( R );
#I Circle : <R> is not a radical algebra, computing circle units ...
#I Circle : searching generators for adjoint group ...
<group of size 4 with 2 generators>
gap> SetInfoLevel( InfoCircle, 0 );
]]>
</Example>
</Description>
</ManSection>
</Section>
</Chapter>