CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it

Path: gap4r8 / doc / tut / chap8.txt
Views: 418346
1
2
8 Operations and Methods
3
4
5
8.1 Attributes
6
7
In the preceding chapters, we have seen how to obtain information about
8
mathematical objects in GAP: We have to pass the object as an argument to a
9
function. For example, if G is a group one can call Size( G ), and the
10
function will return a value, in our example an integer which is the size of
11
G. Computing the size of a group generally requires a substantial amount of
12
work, therefore it seems desirable to store the size somewhere once it has
13
been calculated. You should imagine that GAP stores the size in some place
14
associated with the object G when Size( G ) is executed for the first time,
15
and if this function call is executed again later, the size is simply looked
16
up and returned, without further computation.
17
18
This means that the behavior of the function Size (Reference: Size) has to
19
depend on whether the size for the argument G is already known, and if not,
20
that the size must be stored after it has been calculated. These two extra
21
tasks are done by two other functions that accompany Size( G ), namely the
22
tester HasSize( G ) and the setter SetSize( G, size ). The tester returns
23
true or false according to whether G has already stored its size, and the
24
setter puts size into a place from where G can directly look it up. The
25
function Size (Reference: Size) itself is called the getter, and from the
26
preceding discussion we see that there must really be at least two methods
27
for the getter: One method is used when the tester returns false; it is the
28
method which first does the real computation and then executes the setter
29
with the computed value. A second method is used when the tester returns
30
true; it simply returns the stored value. This second method is also called
31
the system getter. GAP functions for which several methods can be available
32
are called operations, so Size (Reference: Size) is an example of an
33
operation.
34
35
 Example 
36
gap> G := Group(List([1..3], i-> Random(SymmetricGroup(53))));;
37
gap> Size( G ); time > 0; # the time may of course vary on your machine
38
4274883284060025564298013753389399649690343788366813724672000000000000
39
true
40
gap> Size( G ); time;
41
4274883284060025564298013753389399649690343788366813724672000000000000
42
0
43

44
45
The convenient thing for the user is that GAP automatically chooses the
46
right method for the getter, i.e., it calls a real-work getter at most once
47
and the system getter in all subsequent occurrences. At most once because
48
the value of a function call like Size( G ) can also be set for G before the
49
getter is called at all; for example, one can call the setter directly if
50
one knows the size.
51
52
The size of a group is an example of a class of things which in GAP are
53
called attributes. Every attribute in GAP is represented by a triple of a
54
getter, a setter and a tester. When a new attribute is declared, all three
55
functions are created together and the getter contains references to the
56
other two. This is necessary because when the getter is called, it must
57
first consult the tester, and perhaps execute the setter in the end.
58
Therefore the getter could be implemented as follows:
59
60
 Example 
61
getter := function( obj )
62
local value;
63
 if tester( obj ) then
64
 value := system_getter( obj );
65
 else
66
 value := real_work_getter( obj );
67
 setter( obj, value );
68
 fi;
69
 return value;
70
end;
71

72
73
The only function which depends on the mathematical nature of the attribute
74
is the real-work getter, and this is of course what the programmer of an
75
attribute has to install. In both cases, the getter returns the same value,
76
which we also call the value of the attribute (properly: the value of the
77
attribute for the object obj). By the way: The names for setter and tester
78
of an attribute are always composed from the prefix Set resp. Has and the
79
name of the getter.
80
81
As a (not typical) example, note that the GAP function Random (Reference:
82
Random), although it takes only one argument, is of course not an attribute,
83
because otherwise the first random element of a group would be stored by the
84
setter and returned over and over again by the system getter every time
85
Random (Reference: Random) is called in the sequel.
86
87
There is a general important rule about attributes: Once the value of an
88
attribute for an object has been set, it cannot be reset, i.e., it cannot be
89
changed any more. This is achieved by having two methods not only for the
90
getter but also for the setter: If an object already has an attribute value
91
stored, i.e., if the tester returns true, the setter simply does nothing.
92
93
 Example 
94
gap> G := SymmetricGroup(8);; Size(G);
95
40320
96
gap> SetSize( G, 0 ); Size( G );
97
40320
98

99
100
Summary. In this section we have introduced attributes as triples of getter,
101
setter and tester and we have explained how these three functions work
102
together behind the scene to provide automatic storage and look-up of values
103
that have once been calculated. We have seen that there can be several
104
methods for the same function among which GAP automatically selects an
105
appropriate one.
106
107
108
8.2 Properties and Filters
109
110
Certain attributes, like IsAbelian (Reference: IsAbelian), are
111
boolean-valued. Such attributes are known to GAP as properties, because
112
their values are stored in a slightly different way. A property also has a
113
getter, a setter and a tester, but in this case, the getter as well as the
114
tester returns a boolean value. Therefore GAP stores both values in the same
115
way, namely as bits in a boolean list, thereby treating property getters and
116
all testers (of attributes or properties) uniformly. These boolean-valued
117
functions are called filters. You can imagine a filter as a switch which is
118
set either to true or to false. For every GAP object there is a boolean list
119
which has reserved a bit for every filter GAP knows about. Strictly
120
speaking, there is one bit for every simple filter, and these simple filters
121
can be combined with and to form other filters (which are then true if and
122
only if all the corresponding bits are set to true). For example, the filter
123
IsPermGroup and IsSolvableGroup is made up from several simple filters.
124
125
Since they allow only two values, the bits which represent filters can be
126
compared very quickly, and the scheme by which GAP chooses the method, e.g.,
127
for a getter or a setter (as we have seen in the previous section), is
128
mostly based on the examination of filters, not on the examination of other
129
attribute values. Details of this method selection are described in
130
chapter 'Reference: Method Selection'.
131
132
We only present the following rule of thumb here: Each installed method for
133
an attribute, say Size (Reference: Size), has a required filter, which is
134
made up from certain simple filters which must yield true for the argument
135
obj for this method to be applicable. To execute a call of Size( obj ), GAP
136
selects among all applicable methods the one whose required filter combines
137
the most simple filters; the idea behind is that the more an algorithm
138
requires of obj, the more efficient it is expected to be. For example, if
139
obj is a permutation group that is not (known to be) solvable, a method with
140
required filter IsPermGroup and IsSolvableGroup is not applicable, whereas a
141
method with required filter IsPermGroup (Reference: IsPermGroup) can be
142
chosen. On the other hand, if obj was known to be solvable, the method with
143
required filter IsPermGroup and IsSolvableGroup would be preferred to the
144
one with required filter IsPermGroup (Reference: IsPermGroup).
145
146
It may happen that a method is applicable for a given argument but cannot
147
compute the desired value. In such cases, the method will execute the
148
statement TryNextMethod();, and GAP calls the next applicable method. For
149
example, [Sim90] describes an algorithm to compute the size of a solvable
150
permutation group, which can be used also to decide whether or not a
151
permutation group is solvable. Suppose that the function size_solvable
152
implements this algorithm, and that is returns the order of the group if it
153
is solvable and fail otherwise. Then we can install the following method for
154
Size (Reference: Size) with required filter IsPermGroup (Reference:
155
IsPermGroup).
156
157
 Example 
158
function( G )
159
local value;
160
 value := size_solvable( G );
161
 if value <> fail then return value;
162
 else TryNextMethod(); fi;
163
end;
164

165
166
This method can then be tried on every permutation group (whether known to
167
be solvable or not), and it would include a mandatory solvability test.
168
169
If no applicable method (or no next applicable method) is found, GAP stops
170
with an error message of the form
171
172
 Example 
173
Error, no method found! For debugging hints type ?Recovery from NoMethodFound
174
Error, no 1st choice method found for `Size' on 1 arguments called from
175
... lines deleted here ...
176

177
178
You would get an error message as above if you asked for Size( 1 ). The
179
message simply says that there is no method installed for calculating the
180
size of 1. Section 'Reference: Recovery from NoMethodFound-Errors' contains
181
more information on how to deal with these messages.
182
183
Summary. In this section we have introduced properties as special
184
attributes, and filters as the general concept behind property getters and
185
attribute testers. The values of the filters of an object govern how the
186
object is treated in the selection of methods for operations.
187
188
189
8.3 Immediate and True Methods
190
191
In the example in Section 8.2, we have mentioned that the operation Size
192
(Reference: Size) has a method for solvable permutation groups that is so
193
far superior to the method for general permutation groups that it seems
194
worthwhile to try it even if nothing is known about solvability of the group
195
of which the Size (Reference: Size) is to be calculated. There are other
196
examples where certain methods are even cheaper to execute. For example, if
197
the size of a group is known it is easy to check whether it is odd, and if
198
so, the Feit-Thompson theorem allows us to set IsSolvableGroup (Reference:
199
IsSolvableGroup) to true for this group. GAP utilizes this celebrated
200
theorem by having an immediate method for IsSolvableGroup (Reference:
201
IsSolvableGroup) with required filter HasSize which checks parity of the
202
size and either sets IsSolvableGroup (Reference: IsSolvableGroup) or does
203
nothing, i.e., calls TryNextMethod(). These immediate methods are executed
204
automatically for an object whenever the value of a filter changes, so
205
solvability of a group will automatically be detected when an odd size has
206
been calculated for it (and therefore the value of HasSize for that group
207
has changed to true).
208
209
Some methods are even more immediate, because they do not require any
210
calculation at all: They allow a filter to be set if another filter is also
211
set. In other words, they model a mathematical implication like IsGroup and
212
IsCyclic implies IsSolvableGroup and such implications can be installed in
213
GAP as true methods. To execute true methods, GAP only needs to do some
214
bookkeeping with its filters, therefore true methods are much faster than
215
immediate methods.
216
217
How immediate and true methods are installed is described in 'Reference:
218
Immediate Methods' and 'Reference: Logical Implications'.
219
220
221
8.4 Operations and Method Selection
222
223
The method selection is not only used to select methods for attribute
224
getters but also for arbitrary operations, which can have more than one
225
argument. In this case, there is a required filter for each argument (which
226
must yield true for the corresponding arguments).
227
228
Additionally, a method with at least two arguments may require a certain
229
relation between the arguments, which is expressed in terms of the families
230
of the arguments. For example, the methods for ConjugateGroup( grp, elm )
231
require that elm lies in the family of elements from which grp is made,
232
i.e., that the family of elm equals the elements family of grp.
233
234
For permutation groups, the situation is quite easy: all permutations form
235
one family, PermutationsFamily (Reference: PermutationsFamily), and each
236
collection of permutations, for example each permutation group, each coset
237
of a permutation group, or each dense list of permutations, lies in
238
CollectionsFamily( PermutationsFamily ).
239
240
For other kinds of group elements, the situation can be different. Every
241
call of FreeGroup (Reference: FreeGroup) constructs a new family of free
242
group elements. GAP refuses to compute One( FreeGroup( 1 ) ) * One(
243
FreeGroup( 1 ) ) because the two operands of the multiplication lie in
244
different families and no method is installed for this case.
245
246
For further information on family relations, see 'Reference: Families'.
247
248
If you want to know which properties are already known for an object obj, or
249
which properties are known to be true, you can use the functions
250
KnownPropertiesOfObject( obj ) resp. KnownTruePropertiesOfObject( obj ).
251
This will print a list of names of properties. These names are also the
252
identifiers of the property getters, by which you can retrieve the value of
253
the properties (and confirm that they are really true). Analogously, there
254
is the function KnownAttributesOfObject (Reference: KnownAttributesOfObject)
255
which lists the names of the known attributes, leaving out the properties.
256
257
Since GAP lets you know what it already knows about an object, it is only
258
natural that it also lets you know what methods it considers applicable for
259
a certain method, and in what order it will try them (in case
260
TryNextMethod() occurs). ApplicableMethod( opr, [ arg_1, arg_2, ... ] )
261
returns the first applicable method for the call opr( arg_1, arg_2, ... ).
262
More generally, ApplicableMethod( opr, [ ... ], 0, nr ) returns the nrth
263
applicable method (i.e., the one that would be chosen after nr-1 calls of
264
TryNextMethod) and if nr = "all", the sorted list of all applicable methods
265
is returned. For details, see 'Reference: Applicable Methods and Method
266
Selection'.
267
268
If you want to see which methods are chosen for certain operations while GAP
269
code is being executed, you can call the function TraceMethods (Reference:
270
TraceMethods for a list of operations) with a list of these operations as
271
arguments.
272
273
 Example 
274
gap> TraceMethods( [ Size ] );
275
gap> g:= Group( (1,2,3), (1,2) );; Size( g );
276
#I Size: for a permutation group
277
#I Setter(Size): system setter
278
#I Size: system getter
279
#I Size: system getter
280
6
281

282
283
The system getter is called once to fetch the freshly computed value for
284
returning to the user. The second call is triggered by an immediate method.
285
To find out by which, we can trace the immediate methods by saying
286
TraceImmediateMethods( true ).
287
288
 Example 
289
gap> TraceImmediateMethods( true );
290
gap> g:= Group( (1,2,3), (1,2) );;
291
#I immediate: Size
292
#I immediate: IsCyclic
293
#I immediate: IsCommutative
294
#I immediate: IsTrivial
295
gap> Size( g );
296
#I Size: for a permutation group
297
#I immediate: IsNonTrivial
298
#I immediate: Size
299
#I immediate: IsFreeAbelian
300
#I immediate: IsTorsionFree
301
#I immediate: IsNonTrivial
302
#I immediate: GeneralizedPcgs
303
#I Setter(Size): system setter
304
#I Size: system getter
305
#I immediate: IsPerfectGroup
306
#I Size: system getter
307
#I immediate: IsEmpty
308
6
309
gap> TraceImmediateMethods( false );
310
gap> UntraceMethods( [ Size ] );
311

312
313
The last two lines switch off tracing again. We now see that the system
314
getter was called by the immediate method for IsPerfectGroup (Reference:
315
IsPerfectGroup). Also the above-mentioned immediate method for
316
IsSolvableGroup (Reference: IsSolvableGroup) was not used because the
317
solvability of g was already found out during the size calculation (cf. the
318
example in Section 8.2).
319
320
Summary. In this section and the last we have looked some more behind the
321
scenes and seen that GAP automatically executes immediate and true methods
322
to deduce information about objects that is cheaply available. We have seen
323
how this can be supervised by tracing the methods.
324
325
326