Domain is GAP's name for structured sets. We already saw examples
of domains in the previous sections. For example, the groups s8
and
a8
in sections About Groups and About Operations of Groups are
domains. Likewise the fields in section About Fields are domains.
Categories are sets of domains. For example, the set of all groups
forms a category, as does the set of all fields.
In those sections we treated the domains as black boxes. They were
constructed by special functions such as Group
and GaloisField
, and
they could be passed as arguments to other functions such as Size
and
Orbits
.
In this section we will also treat domains as black boxes. We will describe how domains are created in general and what functions are applicable to all domains. Next we will show how domains with the same structure are grouped into categories and will give an overview of the categories that are available. Then we will discuss how the organization of the GAP library around the concept of domains and categories is reflected in this manual. In a later section we will open the black boxes and give an overview of the mechanism that makes all this work (see About the Implementation of Domains).
The first thing you must know is how you can obtain domains. You have basically three possibilities. You can use the domains that are predefined in the library, you can create new domains with domain constructors, and you can use the domains returned by many library functions. We will now discuss those three possibilities in turn.
The GAP library predefines some domains. That means that there is a global variable whose value is this domain. The following example shows some of the more important predefined domains.
gap> Integers; Integers # the ring of all integers gap> Size( Integers ); "infinity" gap> GaussianRationals; GaussianRationals # the field of all Gaussian gap> (1/2+E(4)) in GaussianRationals; true # 'E(4)' is {\GAP}\'s name for the complex root of -1 gap> Permutations; Permutations # the domain of all permutations
Note that GAP prints those domains using the name of the global variable.
You can create new domains using domain constructors such as Group
,
Field
, etc. A domain constructor is a function that takes a certain
number of arguments and returns the domain described by those arguments.
For example, Group
takes an arbitrary number of group elements (of the
same type) and returns the group generated by those elements.
gap> gf16 := GaloisField( 16 ); GF(2^4) # the finite field with 16 elements gap> Intersection( gf16, GaloisField( 64 ) ); GF(2^2) gap> a5 := Group( (1,2,3), (3,4,5) ); Group( (1,2,3), (3,4,5) ) # the alternating group on 5 points gap> Size( a5 ); 60
Again GAP prints those domains using more or less the expression that you entered to obtain the domain.
As with groups (see About Groups) a name can be assigned to an
arbitrary domain D with the assignment D.name := string;
, and
GAP will use this name from then on in the output.
Many functions in the GAP library return domains. In the last example
you already saw that Intersection
returned a finite field domain.
Below are more examples.
gap> GaloisGroup( gf16 ); Group( FrobeniusAutomorphism( GF(2^4) ) ) gap> SylowSubgroup( a5, 2 ); Subgroup( Group( (1,2,3), (3,4,5) ), [ (2,4)(3,5), (2,3)(4,5) ] )
The distinction between domain constructors and functions that return domains is a little bit arbitrary. It is also not important for the understanding of what follows. If you are nevertheless interested, here are the principal differences. A constructor performs no computation, while a function performs a more or less complicated computation. A constructor creates the representation of the domain, while a function relies on a constructor to create the domain. A constructor knows the dirty details of the domain's representation, while a function may be independent of the domain's representation. A constructor may appear as printed representation of a domain, while a function usually does not.
After showing how domains are created, we will now discuss what you can
do with domains. You can assign a domain to a variable, put a domain
into a list or into a record, pass a domain as argument to a function,
and return a domain as result of a function. In this regard there is no
difference between an integer value such as 17 and a domain such as
Group( (1,2,3), (3,4,5) )
. Of course many functions will signal an
error when you call them with domains as arguments. For example, Gcd
does not accept two groups as arguments, because they lie in no Euclidean
ring.
There are some functions that accept domains of any type as their arguments. Those functions are called the set theoretic functions. The full list of set theoretic functions is given in chapter Domains.
Above we already used one of those functions, namely Size
. If you look
back you will see that we applied Size
to the domain Integers
, which
is a ring, and the domain a5
, which is a group. Remember that a domain
was a structured set. The size of the domain is the number of elements
in the set. Size
returns this number or the string "infinity"
if
the domain is infinite. Below are more examples.
gap> Size( GaussianRationals ); "infinity" # this string is returned for infinite domains gap> Size( SylowSubgroup( a5, 2 ) ); 4
IsFinite( D )
returns true
if the domain D is finite and false
otherwise. You could also test if a domain is finite using Size( D )
< "infinity"
(GAP evaluates n < "infinity"
to true
for
any number n). IsFinite
is more efficient. For example, if D is a
permutation group, IsFinite( D )
can immediately return true
, while
Size( D )
may take quite a while to compute the size of D.
The other function that you already saw is Intersection
. Above we
computed the intersection of the field with 16 elements and the field
with 64 elements. The following example is similar.
gap> Intersection( a5, Group( (1,2), (1,2,3,4) ) ); Group( (2,3,4), (1,2)(3,4) ) # alternating group on 4 points
In general Intersection
tries to return a domain. In general this is
not possible however. Remember that a domain is a structured set. If
the two domain arguments have different structure the intersection may
not have any structure at all. In this case Intersection
returns the
result as a proper set, i.e., as a sorted list without holes and
duplicates. The following example shows such a case. ConjugacyClass
returns the conjugacy class of (1,2,3,4,5)
in the alternating group on
6 points as a domain. If we intersect this class with the symmetric
group on 5 points we obtain a proper set of 12 permutations, which is
only one half of the conjugacy class of 5 cycles in s5
.
gap> a6 := Group( (1,2,3), (2,3,4,5,6) ); Group( (1,2,3), (2,3,4,5,6) ) gap> class := ConjugacyClass( a6, (1,2,3,4,5) ); ConjugacyClass( Group( (1,2,3), (2,3,4,5,6) ), (1,2,3,4,5) ) gap> Size( class ); 72 gap> s5 := Group( (1,2), (2,3,4,5) ); Group( (1,2), (2,3,4,5) ) gap> Intersection( class, s5 ); [ (1,2,3,4,5), (1,2,4,5,3), (1,2,5,3,4), (1,3,5,4,2), (1,3,2,5,4), (1,3,4,2,5), (1,4,3,5,2), (1,4,5,2,3), (1,4,2,3,5), (1,5,4,3,2), (1,5,2,4,3), (1,5,3,2,4) ]
You can intersect arbitrary domains as the following example shows.
gap> Intersection( Integers, a5 ); [ ] # the empty set
Note that we optimized Intersection
for typical cases, e.g., computing
the intersection of two permutation groups, etc. The above computation
is done with a very simple--minded method, all elements of a5
are
listed (with Elements
, described below), and for each element
Intersection
tests whether it lies in Integers
(with in
, described
below). So the same computation with the alternating group on 10 points
instead of a5
will probably exhaust your patience.
Just as Intersection
returns a proper set occasionally, it also accepts
proper sets as arguments. Intersection
also takes an arbitrary number
of arguments. And finally it also accepts a list of domains or sets to
intersect as single argument.
gap> Intersection( a5, [ (1,2), (1,2,3), (1,2,3,4), (1,2,3,4,5) ] ); [ (1,2,3), (1,2,3,4,5) ] gap> Intersection( [2,4,6,8,10], [3,6,9,12,15], [5,10,15,20,25] ); [ ] gap> Intersection( [ [1,2,4], [2,3,4], [1,3,4] ] ); [ 4 ]
The function Union
is the obvious counterpart of Intersection
. Note
that Union
usually does not return a domain. This is because the
union of two domains, even of the same type, is usually not again a
domain of that type. For example, the union of two subgroups is a
subgroup if and only if one of the subgroups is a subset of the other.
Of course this is exactly the reason why Union
is less important than
Intersection
in algebra.
Because domains are structured sets there ought to be a membership test
that tests whether an object lies in this domain or not. This is not
implemented by a function, instead the operator in
is used. elm in
D
returns true
if the element elm lies in the domain D and
false
otherwise. We already used the in
operator above when we
tested whether 1/2 + E(4)
lies in the domain of Gaussian integers.
gap> (1,2,3) in a5; true gap> (1,2) in a5; false gap> (1,2,3,4,5,6,7) in a5; false gap> 17 in a5; false # of course an integer does not lie in a permutation group gap> a5 in a5; false
As you can see in the last example, in
only implements the membership
test. It does not allow you to test whether a domain is a subset of
another domain. For such tests the function IsSubset
is available.
gap> IsSubset( a5, a5 ); true gap> IsSubset( a5, Group( (1,2,3) ) ); true gap> IsSubset( Group( (1,2,3) ), a5 ); false
In the above example you can see that IsSubset
tests whether the second
argument is a subset of the first argument. As a general rule GAP
library functions take as first arguments those arguments that are in
some sense larger or more structured.
Suppose that you want to loop over all elements of a domain. For
example, suppose that you want to compute the set of element orders of
elements in the group a5
. To use the for
loop you need a list of
elements in the domain D, because for var in D do statements od
will not work. The function Elements
does exactly that. It takes a
domain D and returns the proper set of elements of D.
gap> Elements( Group( (1,2,3), (2,3,4) ) ); [ (), (2,3,4), (2,4,3), (1,2)(3,4), (1,2,3), (1,2,4), (1,3,2), (1,3,4), (1,3)(2,4), (1,4,2), (1,4,3), (1,4)(2,3) ] gap> ords := [];; gap> for elm in Elements( a5 ) do > Add( ords, Order( a5, elm ) ); > od; gap> Set( ords ); [ 1, 2, 3, 5 ] gap> Set( List( Elements( a5 ), elm -> Order( a5, elm ) ) ); [ 1, 2, 3, 5 ] # an easier way to compute the set of orders
Of course, if you apply Elements
to an infinite domain, Elements
will
signal an error. It is also not a good idea to apply Elements
to very
large domains because the list of elements will take much space and
computing this large list will probably exhaust your patience.
gap> Elements( GaussianIntegers ); Error, the ring <R> must be finite to compute its elements in D.operations.Elements( D ) called from Elements( GaussianIntegers ) called from main loop brk> quit;
There are a few more set theoretic functions. See chapter Domains for a complete list.
All the set theoretic functions treat the domains as if they had no structure. Now a domain is a structured set (excuse us for repeating this again and again, but it is really important to get this across). If the functions ignore the structure than they are effectively viewing a domain only as the set of elements.
In fact all set theoretic functions also accept proper sets, i.e., sorted
lists without holes and duplicates as arguments (we already mentioned
this for Intersection
). Also set theoretic functions may occasionally
return proper sets instead of domains as result.
This equivalence of a domain and its set of elements is particularly
important for the definition of equality of domains. Two domains D and
E are equal (in the sense that D = E
evaluates to true
) if and
only if the set of elements of D is equal to the set of elements of E
(as returned by Elements( D )
and Elements( E )
). As a special
case either of the operands of =
may also be a proper set, and the
value is true
if this set is equal to the set of elements of the
domain.
gap> a4 := Group( (1,2,3), (2,3,4) ); Group( (1,2,3), (2,3,4) ) gap> elms := Elements( a4 ); [ (), (2,3,4), (2,4,3), (1,2)(3,4), (1,2,3), (1,2,4), (1,3,2), (1,3,4), (1,3)(2,4), (1,4,2), (1,4,3), (1,4)(2,3) ] gap> elms = a4; true
However the following example shows that this does not imply that all functions return the same answer for two domains (or a domain and a proper set) that are equal. This is because those function may take the structure into account.
gap> IsGroup( a4 ); true gap> IsGroup( elms ); false gap> Intersection( a4, Group( (1,2), (1,2,3) ) ); Group( (1,2,3) ) gap> Intersection( elms, Group( (1,2), (1,2,3) ) ); [ (), (1,2,3), (1,3,2) ] # this is not a group gap> last = last2; true # but it is equal to the above result gap> Centre( a4 ); Subgroup( Group( (1,2,3), (2,3,4) ), [ ] ) gap> Centre( elms ); Error, <struct> must be a record in Centre( elms ) called from main loop brk> quit;
Generally three things may happen if you have two domains D and E
that are equal but have different structure (or a domain D and a set
E that are equal). First a function that tests whether a domain has a
certain structure may return true
for D and false
for E. Second
a function may return a domain for D and a proper set for E. Third a
function may work for D and fail for E, because it requires the
structure.
A slightly more complex example for the second case is the following.
gap> v4 := Subgroup( a4, [ (1,2)(3,4), (1,3)(2,4) ] ); Subgroup( Group( (1,2,3), (2,3,4) ), [ (1,2)(3,4), (1,3)(2,4) ] ) gap> v4.name := "v4";; gap> rc := v4 * (1,2,3); (v4*(2,4,3)) gap> lc := (1,2,3) * v4; ((1,2,3)*v4) gap> rc = lc; true gap> rc * (1,3,2); (v4*()) gap> lc * (1,3,2); [ (1,3)(2,4), (), (1,2)(3,4), (1,4)(2,3) ] gap> last = last2; false
The two domains rc
and lc
(yes, cosets are domains too) are equal,
because they have the same set of elements. However if we multiply both
with (1,3,2)
we obtain the trivial right coset for rc
and a list for
lc
. The result for lc
is not a proper set, because it is not
sorted, therefore =
evaluates to false
. (For the curious. The
multiplication of a left coset with an element from the right will
generally not yield another coset, i.e., nothing that can easily be
represented as a domain. Thus to multiply lc
with (1,3,2)
GAP
first converts lc
to the set of its elements with Elements
. But the
definition of multiplication requires that a list l multiplied by an
element e yields a new list n such that each element n[i]
in
the new list is the product of the element l[i]
at the same
position of the operand list l with e.)
Note that the above definition only defines what the result of the equality comparison of two domains D and E should be. It does not prescribe that this comparison is actually performed by listing all elements of D and E. For example, if D and E are groups, it is sufficient to check that all generators of D lie in E and that all generators of E lie in D. If GAP would really compute the whole set of elements, the following test could not be performed on any computer.
gap> Group( (1,2), (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18) ) > = Group( (17,18), (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18) ); true
If we could only apply the set theoretic functions to domains, domains
would be of little use. Luckily this is not so. We already saw that we
could apply GaloisGroup
to the finite field with 16 elements, and
SylowSubgroup
to the group a5
. But those functions are not
applicable to all domains. The argument of GaloisGroup
must be a
field, and the argument of SylowSubgroup
must be a group.
A category is a set of domains. So we say that the argument of
GaloisGroup
must be an element of the category of fields, and the
argument of SylowSubgroup
must be an element of the category of groups.
The most important categories are rings, fields, groups, and
vector spaces. Which category a domain belongs to determines which
functions are applicable to this domain and its elements. We want to
emphasize the each domain belongs to one and only one category. This
is necessary because domains in different categories have, sometimes
incompatible, representations.
Note that the categories only exist conceptually. That means that there
is no GAP object for the categories, e.g., there is no object
Groups
. For each category there exists a function that tests whether a
domain is an element of this category.
gap> IsRing( gf16 ); false gap> IsField( gf16 ); true gap> IsGroup( gf16 ); false gap> IsVectorSpace( gf16 ); false
Note that of course mathematically the field gf16
is also a ring and a
vector space. However in GAP a domain can only belong to one
category. So a domain is conceptually a set of elements with one
structure, e.g., a field structure. That the same set of elements may
also support a different structure, e.g., a ring or vector space
structure, can not be represented by this domain. So you need a
different domain to represent this different structure. (We are planning
to add functions that changes the structure of a domain, e.g. AsRing(
field )
should return a new domain with the same elements as field
but with a ring structure.)
Domains may have certain properties. For example a ring may be
commutative and a group may be nilpotent. Whether a domain has a certain
property Property can be tested with the function IsProperty
.
gap> IsCommutativeRing( GaussianIntegers ); true gap> IsNilpotent( a5 ); false
There are also similar functions that test whether a domain (especially a
group) is represented in a certain way. For example IsPermGroup
tests
whether a group is represented as a permutation group.
gap> IsPermGroup( a5 ); true gap> IsPermGroup( a4 / v4 ); false # 'a4 / v4' is represented as a generic factor group
There is a slight difference between a function such as IsNilpotent
and
a function such as IsPermGroup
. The former tests properties of an
abstract group and its outcome is independent of the representation of
that group. The latter tests whether a group is given in a certain
representation.
This (rather philosophical) issue is further complicated by the fact that
sometimes representations and properties are not independent. This is
especially subtle with IsSolvable
(see IsSolvable) and IsAgGroup
(see IsAgGroup). IsSolvable
tests whether a group G is solvable.
IsAgGroup
tests whether a group G is represented as a finite
polycyclic group, i.e., by a finite presentation that allows to
Finite Polycyclic Groups). Of course every finite polycyclic group is
solvable, so IsAgGroup( G )
implies IsSolvable( G )
. On the
other hand IsSolvable( G )
does not imply IsAgGroup( G )
,
because, even though each solvable group can be represented as a finite
polycyclic group, it need not, e.g., it could also be represented as a
permutation group.
The organization of the manual follows the structure of domains and categories.
After the description of the programming language and the environment chapter Domains describes the domains and the functions applicable to all domains.
Next come the chapters that describe the categories rings, fields, groups, and vector spaces.
The remaining chapters describe GAP's data--types and the domains one can make with those elements of those data-types. The order of those chapters roughly follows the order of the categories. The data--types whose elements form rings and fields come first (e.g., integers and finite fields), followed by those whose elements form groups (e.g., permutations), and so on. The data--types whose elements support little or no algebraic structure come last (e.g., booleans). In some cases there may be two chapters for one data--type, one describing the elements and the other describing the domains made with those elements (e.g., permutations and permutation groups).
The GAP manual not only describes what you can do, it also gives some hints how GAP performs its computations. However, it can be tricky to find those hints. The index of this manual can help you.
Suppose that you want to intersect two permutation groups. If you read
the section that describes the function Intersection
(see
Intersection) you will see that the last paragraph describes the
default method used by Intersection
. Such a last paragraph that
describes the default method is rather typical. In this case it says
that Intersection
computes the proper set of elements of both domains
and intersect them. It also says that this method is often overlaid
with a more efficient one. You wonder whether this is the case for
permutation groups. How can you find out? Well you look in the index
under Intersection. There you will find a reference Intersection, for
permutation groups to section Set Functions for Permutation Groups
(see Set Functions for Permutation Groups). This section tells you
that Intersection
uses a backtrack for permutation groups (and cites a
book where you can find a description of the backtrack).
Let us now suppose that you intersect two factor groups. There is no
reference in the index for Intersection, for factor groups. But there
is a reference for Intersection, for groups to the section Set
Functions for Groups (see Set Functions for Groups). Since this is
the next best thing, look there. This section further directs you to the
section Intersection for Groups (see Intersection for Groups). This
section finally tells you that Intersection
computes the intersection
of two groups G and H as the stabilizer in G of the trivial coset
of H under the operation of G on the right cosets of H.
In this section we introduced domains and categories. You have learned that a domain is a structured set, and that domains are either predefined, created by domain constructors, or returned by library functions. You have seen most functions that are applicable to all domains. Those functions generally ignore the structure and treat a domain as the set of its elements. You have learned that categories are sets of domains, and that the category a domain belongs to determines which functions are applicable to this domain.
More information about domains can be found in chapter Domains. Chapters Rings, Fields, Groups, and Vector Spaces define the About the Implementation of Domains opens that black boxes and shows how all this works.
GAP 3.4.4