<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>