Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modules/finite_submodule_iter.pyx
8815 views
1
r"""
2
Iterators over finite submodules of a `\ZZ`-module
3
4
We iterate over the elements of a finite `\ZZ`-module. The action
5
of `\ZZ` must be the natural one.
6
7
This class is intended to provide optimizations for the
8
:meth:`sage.free_module.FreeModule_generic:__iter__` method.
9
10
AUTHORS:
11
12
- Thomas Feulner (2012-08-31): initial version
13
- Punarbasu Purkayastha (2012-11-09): replaced the loop with recursion
14
- Thomas Feulner (2012-11-09): added functionality to enumerate cosets, FiniteFieldsubspace_projPoint_iterator
15
16
EXAMPLES::
17
18
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
19
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
20
sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])
21
sage: list(iter)
22
[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]
23
24
There is a specialization for subspaces over finite fields::
25
26
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator
27
sage: A = random_matrix(GF(4, 'a'), 5, 100)
28
sage: iter = FiniteFieldsubspace_iterator(A)
29
sage: len(list(iter))
30
1024
31
32
The module also allows the iteration over cosets::
33
34
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator
35
sage: A = random_matrix(GF(4, 'a'), 5, 100)
36
sage: v = random_vector(GF(4, 'a'), 100)
37
sage: iter = FiniteFieldsubspace_iterator(A, v)
38
sage: len(list(iter))
39
1024
40
41
TESTS::
42
43
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator
44
sage: A = random_matrix(GF(4, 'a'), 5, 100)
45
sage: iter = FiniteFieldsubspace_iterator(A)
46
sage: TestSuite(iter).run(skip='_test_pickling')
47
48
In a second step, we will replace all calls to ``__iter__`` for finite submodules. This
49
will result in improved running times::
50
51
sage: A = random_matrix(GF(2), 15, 100)
52
sage: X = A.row_space()
53
sage: x = [0 for _ in X] #long time #takes 7.12 seconds
54
sage: y = [0 for _ in FiniteFieldsubspace_iterator(A)] # takes 0.05 seconds
55
sage: sorted(x) == sorted(y) #long time
56
True
57
"""
58
59
#*****************************************************************************
60
# Copyright (C) 2012 Thomas Feulner <[email protected]>
61
#
62
# Distributed under the terms of the GNU General Public License (GPL)
63
# as published by the Free Software Foundation; either version 2 of
64
# the License, or (at your option) any later version.
65
# http://www.gnu.org/licenses/
66
#*****************************************************************************
67
68
include "sage/ext/stdsage.pxi"
69
70
cdef class FiniteZZsubmodule_iterator:
71
r"""
72
Let `G` be an abelian group and suppose that `(g_0, \ldots, g_n)`
73
is a list of elements of `G`, whose additive orders are equal to `m_i`
74
and `\sum_{i=0}^n x_i g_i = 0` for `x_i \in \ZZ_{m_i}` for
75
`i \in \{0, \dots, n\}` implies `x_i=0` for all `i`.
76
77
This class implements an iterator over the `\ZZ`-submodule
78
`M = \{\sum_{i=0}^n x_i g_i\}`. If the independence condition from
79
above is not fulfilled, we can still use this iterator to run over the
80
elements. In this case the elements will occur multiple times.
81
82
Getting from one element of the submodule to another is performed by
83
one single addition in `G`.
84
85
INPUT:
86
87
- ``basis`` - the elements `(g_0, \ldots, g_n)`
88
- ``orders`` (optional) - the additive_orders `m_i` of `g_i`.
89
- ``coset_rep`` (optional) -- an element of g,
90
if one aims to compute a coset of the `\ZZ`-submodule `M`.
91
92
EXAMPLES::
93
94
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
95
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
96
sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])
97
sage: list(iter)
98
[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]
99
sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3], z)
100
sage: list(iter)
101
[z, x + z, 2*x + z, y + z, x + y + z, 2*x + y + z, 2*y + z, x + 2*y + z, 2*x + 2*y + z]
102
"""
103
104
def __init__(self, basis, order=None, coset_rep=None):
105
"""
106
see :class:`FiniteZZsubmodule_iterator`
107
108
EXAMPLES::
109
110
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
111
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
112
sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])
113
sage: list(iter)
114
[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]
115
"""
116
if order is None:
117
try:
118
order = [b.additive_order() for b in basis]
119
except (AttributeError, NotImplementedError):
120
raise ValueError("Unable to determine the additive order "
121
"of a basis element. Use the optional "
122
"parameter `order`.")
123
124
self._basis = basis[0]
125
self._basis_all = basis
126
self._basis_length = len(basis)
127
self._count = 0
128
129
if coset_rep is None:
130
self._coset_rep = self._basis.parent().zero()
131
else:
132
self._coset_rep = self._basis.parent()(coset_rep)
133
if self._basis_length == 1:
134
self._cw = self._coset_rep
135
else:
136
self._cw = self._basis.parent().zero()
137
self._other_ZZ = FiniteZZsubmodule_iterator(basis[1:], order[1:], coset_rep)
138
139
self._order = order[0]
140
self._other = self._basis.parent().zero() # dummy initialization
141
self._plus = [self._cw] # storing this provides about 20% speedup
142
for _ in range(self._order - 1):
143
self._cw += self._basis
144
self._plus.append(self._cw)
145
146
def __next__(self):
147
"""
148
Return the next submodule element. This will just add/subtract
149
another element of the ``basis``.
150
151
EXAMPLES::
152
153
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
154
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
155
sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])
156
sage: next(iter) #indirect doctest
157
0
158
sage: next(iter) #indirect doctest
159
x
160
"""
161
self._iteration()
162
return self._cw
163
164
def __repr__(self):
165
"""
166
EXAMPLES::
167
168
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
169
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
170
sage: FiniteZZsubmodule_iterator([x,y], [3,3])
171
Iterator over ZZ-submodule generated by [x, y]
172
"""
173
return "Iterator over ZZ-submodule generated by {0}".format(self._basis_all)
174
175
def __iter__(self):
176
"""
177
EXAMPLE::
178
179
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
180
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
181
sage: list(FiniteZZsubmodule_iterator([x,y], [3,3])) #indirect doctest
182
[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]
183
"""
184
return self
185
186
cdef ModuleElement _iteration(FiniteZZsubmodule_iterator self):
187
"""
188
This is the core implementation of the iteration.
189
190
EXAMPLES::
191
192
sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator
193
sage: F.<x,y,z> = FreeAlgebra(GF(3),3)
194
sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])
195
sage: next(iter) #indirect doctest
196
0
197
sage: print next(iter), next(iter), next(iter) #indirect doctest
198
x 2*x y
199
"""
200
if self._basis_length == 1:
201
if self._count < self._order:
202
self._cw = self._plus[self._count]
203
self._count += 1
204
else:
205
raise StopIteration
206
else:
207
if self._count == 0 or self._count == self._order:
208
self._other = self._other_ZZ.next()
209
self._cw = < ModuleElement > self._other
210
self._count = 1
211
else:
212
self._cw = self._other + self._plus[self._count]
213
self._count += 1
214
215
216
cdef class FiniteFieldsubspace_iterator(FiniteZZsubmodule_iterator):
217
"""
218
This class implements an iterator over the subspace of a vector
219
space over a finite field. The subspace is generated by ``basis``.
220
221
INPUT:
222
223
- ``basis`` -- a list of vectors or a matrix with elements over
224
a finite field. If a matrix is provided then it is not checked
225
whether the matrix is full ranked. Similarly, if a list of
226
vectors is provided, then the linear independence of the vectors
227
is not checked.
228
229
- ``coset_rep`` (optional) -- a vector in the same ambient space,
230
if one aims to compute a coset of the vector space given by ``basis``.
231
232
EXAMPLES::
233
234
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator
235
sage: A = random_matrix(GF(2), 10, 100)
236
sage: iter = FiniteFieldsubspace_iterator(A)
237
sage: len(list(iter))
238
1024
239
sage: X = random_matrix(GF(4, 'a'), 7, 100).row_space()
240
sage: s = list(X) # long time (5s on sage.math, 2013)
241
sage: t = list(FiniteFieldsubspace_iterator(X.basis())) # takes 0.31s
242
sage: sorted(t) == sorted(s) # long time
243
True
244
"""
245
246
def __init__(self, basis, coset_rep=None):
247
"""
248
see :class:`FiniteFieldsubspace_iterator`
249
250
EXAMPLES::
251
252
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator
253
sage: A = random_matrix(GF(2), 10, 100)
254
sage: iter = FiniteFieldsubspace_iterator(A)
255
sage: X = list(iter)
256
sage: len(X)
257
1024
258
sage: v = random_vector(GF(2), 100)
259
sage: iter = FiniteFieldsubspace_iterator(A, v)
260
sage: Y = list(iter)
261
sage: len(Y)
262
1024
263
sage: all([Y[i]-X[i]==v for i in range(len(X))])
264
True
265
"""
266
cdef Py_ssize_t d, i, p
267
cdef list pows, order
268
269
F = basis[0].base_ring()
270
P = F.prime_subfield()
271
p = P.order()
272
alpha = F.primitive_element()
273
degree = F.degree()
274
275
pows = [alpha ** i for i in range(degree)]
276
basis = [_p * x for x in basis for _p in pows] # a ZZ_p-basis for the vectorspace
277
order = [p] * (len(basis))
278
279
FiniteZZsubmodule_iterator.__init__(self, basis, order, coset_rep)
280
281
cdef class FiniteFieldsubspace_projPoint_iterator:
282
"""
283
This class implements an iterator over the projective points of a vector
284
space over a finite field. The vector space is generated by ``basis`` and
285
need not to be equal to the full ambient space.
286
287
A projective point (= one dimensional subspace) `P` will be represented by a
288
generator `p`. To ensure that all `p` will be normalized you can set the
289
optional argument ``normalize`` to ``True``.
290
291
INPUT:
292
293
- ``basis`` -- a list of vectors or a matrix with elements over
294
a finite field. If a matrix is provided then it is not checked
295
whether the matrix is full ranked. Similarly, if a list of
296
vectors is provided, then the linear independence of the vectors
297
is not checked.
298
299
- ``normalize`` (optional) -- boolean which indicates if the
300
returned vectors should be normalized, i.e. the first nonzero coordinate
301
is equal to 1. Default: False
302
303
EXAMPLES::
304
305
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator, FiniteFieldsubspace_projPoint_iterator
306
sage: A = random_matrix(GF(4, 'a'), 5, 100)
307
sage: a = len(list(FiniteFieldsubspace_iterator(A)))
308
sage: b = len(list(FiniteFieldsubspace_projPoint_iterator(A)))
309
sage: b == (a-1)/3
310
True
311
312
Prove that the option ``normalize == True`` will only return normalized vectors.
313
314
sage: all([ x.monic() == x for x in FiniteFieldsubspace_projPoint_iterator(A, True) ])
315
True
316
317
TESTS::
318
319
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator
320
sage: A = MatrixSpace(GF(7), 10, 10).one()
321
sage: len(list(FiniteFieldsubspace_projPoint_iterator(A[:0]))) #indirect doctest
322
0
323
sage: len(list(FiniteFieldsubspace_projPoint_iterator(A[:1]))) #indirect doctest
324
1
325
sage: len(list(FiniteFieldsubspace_projPoint_iterator(A[:2]))) #indirect doctest
326
8
327
"""
328
329
def __init__(self, basis, normalize=False):
330
"""
331
see :class:`FiniteFieldsubspace_projPoint_iterator`
332
333
EXAMPLES::
334
335
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator
336
sage: A = random_matrix(GF(4, 'a'), 4, 100)
337
sage: iter = FiniteFieldsubspace_projPoint_iterator(A)
338
sage: len(list(iter))
339
85
340
"""
341
from sage.matrix.constructor import matrix
342
cdef i
343
self._basis = list(basis)
344
self._basis_length = len(self._basis)
345
if normalize:
346
B = matrix(self._basis)
347
B.echelonize()
348
self._basis = B.rows()
349
self._basis.reverse()
350
351
if self._basis_length == 0:
352
self._one_dimensional_case = 2
353
else:
354
self._one_dimensional_case = 1
355
356
def __next__(self):
357
"""
358
Return the next projective point. This will just add/subtract
359
another element of the ``basis`` except for the cases when the rank will
360
increase.
361
362
EXAMPLES::
363
364
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator
365
sage: A = MatrixSpace(GF(3), 10,10).one()
366
sage: iter = FiniteFieldsubspace_projPoint_iterator(A)
367
sage: next(iter) #indirect doctest
368
(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
369
sage: next(iter) #indirect doctest
370
(0, 1, 0, 0, 0, 0, 0, 0, 0, 0)
371
"""
372
if self._one_dimensional_case > 0:
373
if self._one_dimensional_case == 1:
374
self._one_dimensional_case = 2
375
return self._basis[0]
376
else:
377
if self._basis_length > 1:
378
self._it = FiniteFieldsubspace_iterator(self._basis[:1], self._basis[1])
379
self._normalized_pos = 1
380
self._one_dimensional_case = 0
381
else:
382
raise StopIteration
383
try:
384
return self._it.next()
385
except StopIteration:
386
self._normalized_pos += 1
387
if self._normalized_pos == self._basis_length:
388
raise StopIteration
389
else:
390
self._it = FiniteFieldsubspace_iterator(self._basis[:self._normalized_pos], self._basis[self._normalized_pos])
391
return self._it.next()
392
393
def __repr__(self):
394
"""
395
EXAMPLES::
396
397
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator
398
sage: A = MatrixSpace(GF(3), 2, 2).one()
399
sage: FiniteFieldsubspace_projPoint_iterator(A)
400
Iterator over the projective points of a subspace generated by [(1, 0), (0, 1)]
401
"""
402
403
return "Iterator over the projective points of a subspace generated by {0}".format(self._basis)
404
405
def __iter__(self):
406
"""
407
EXAMPLE::
408
409
sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator
410
sage: A = MatrixSpace(GF(3), 10,10).one()
411
sage: len(list(FiniteFieldsubspace_projPoint_iterator(A))) #indirect doctest
412
29524
413
sage: A = MatrixSpace(GF(3), 1,1).one()
414
sage: len(list(FiniteFieldsubspace_projPoint_iterator(A))) #indirect doctest
415
1
416
"""
417
return self
418
419