Path: blob/master/src/sage/modules/finite_submodule_iter.pyx
8815 views
r"""1Iterators over finite submodules of a `\ZZ`-module23We iterate over the elements of a finite `\ZZ`-module. The action4of `\ZZ` must be the natural one.56This class is intended to provide optimizations for the7:meth:`sage.free_module.FreeModule_generic:__iter__` method.89AUTHORS:1011- Thomas Feulner (2012-08-31): initial version12- Punarbasu Purkayastha (2012-11-09): replaced the loop with recursion13- Thomas Feulner (2012-11-09): added functionality to enumerate cosets, FiniteFieldsubspace_projPoint_iterator1415EXAMPLES::1617sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator18sage: F.<x,y,z> = FreeAlgebra(GF(3),3)19sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])20sage: list(iter)21[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]2223There is a specialization for subspaces over finite fields::2425sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator26sage: A = random_matrix(GF(4, 'a'), 5, 100)27sage: iter = FiniteFieldsubspace_iterator(A)28sage: len(list(iter))2910243031The module also allows the iteration over cosets::3233sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator34sage: A = random_matrix(GF(4, 'a'), 5, 100)35sage: v = random_vector(GF(4, 'a'), 100)36sage: iter = FiniteFieldsubspace_iterator(A, v)37sage: len(list(iter))3810243940TESTS::4142sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator43sage: A = random_matrix(GF(4, 'a'), 5, 100)44sage: iter = FiniteFieldsubspace_iterator(A)45sage: TestSuite(iter).run(skip='_test_pickling')4647In a second step, we will replace all calls to ``__iter__`` for finite submodules. This48will result in improved running times::4950sage: A = random_matrix(GF(2), 15, 100)51sage: X = A.row_space()52sage: x = [0 for _ in X] #long time #takes 7.12 seconds53sage: y = [0 for _ in FiniteFieldsubspace_iterator(A)] # takes 0.05 seconds54sage: sorted(x) == sorted(y) #long time55True56"""5758#*****************************************************************************59# Copyright (C) 2012 Thomas Feulner <[email protected]>60#61# Distributed under the terms of the GNU General Public License (GPL)62# as published by the Free Software Foundation; either version 2 of63# the License, or (at your option) any later version.64# http://www.gnu.org/licenses/65#*****************************************************************************6667include "sage/ext/stdsage.pxi"6869cdef class FiniteZZsubmodule_iterator:70r"""71Let `G` be an abelian group and suppose that `(g_0, \ldots, g_n)`72is a list of elements of `G`, whose additive orders are equal to `m_i`73and `\sum_{i=0}^n x_i g_i = 0` for `x_i \in \ZZ_{m_i}` for74`i \in \{0, \dots, n\}` implies `x_i=0` for all `i`.7576This class implements an iterator over the `\ZZ`-submodule77`M = \{\sum_{i=0}^n x_i g_i\}`. If the independence condition from78above is not fulfilled, we can still use this iterator to run over the79elements. In this case the elements will occur multiple times.8081Getting from one element of the submodule to another is performed by82one single addition in `G`.8384INPUT:8586- ``basis`` - the elements `(g_0, \ldots, g_n)`87- ``orders`` (optional) - the additive_orders `m_i` of `g_i`.88- ``coset_rep`` (optional) -- an element of g,89if one aims to compute a coset of the `\ZZ`-submodule `M`.9091EXAMPLES::9293sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator94sage: F.<x,y,z> = FreeAlgebra(GF(3),3)95sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])96sage: list(iter)97[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]98sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3], z)99sage: list(iter)100[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]101"""102103def __init__(self, basis, order=None, coset_rep=None):104"""105see :class:`FiniteZZsubmodule_iterator`106107EXAMPLES::108109sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator110sage: F.<x,y,z> = FreeAlgebra(GF(3),3)111sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])112sage: list(iter)113[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]114"""115if order is None:116try:117order = [b.additive_order() for b in basis]118except (AttributeError, NotImplementedError):119raise ValueError("Unable to determine the additive order "120"of a basis element. Use the optional "121"parameter `order`.")122123self._basis = basis[0]124self._basis_all = basis125self._basis_length = len(basis)126self._count = 0127128if coset_rep is None:129self._coset_rep = self._basis.parent().zero()130else:131self._coset_rep = self._basis.parent()(coset_rep)132if self._basis_length == 1:133self._cw = self._coset_rep134else:135self._cw = self._basis.parent().zero()136self._other_ZZ = FiniteZZsubmodule_iterator(basis[1:], order[1:], coset_rep)137138self._order = order[0]139self._other = self._basis.parent().zero() # dummy initialization140self._plus = [self._cw] # storing this provides about 20% speedup141for _ in range(self._order - 1):142self._cw += self._basis143self._plus.append(self._cw)144145def __next__(self):146"""147Return the next submodule element. This will just add/subtract148another element of the ``basis``.149150EXAMPLES::151152sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator153sage: F.<x,y,z> = FreeAlgebra(GF(3),3)154sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])155sage: next(iter) #indirect doctest1560157sage: next(iter) #indirect doctest158x159"""160self._iteration()161return self._cw162163def __repr__(self):164"""165EXAMPLES::166167sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator168sage: F.<x,y,z> = FreeAlgebra(GF(3),3)169sage: FiniteZZsubmodule_iterator([x,y], [3,3])170Iterator over ZZ-submodule generated by [x, y]171"""172return "Iterator over ZZ-submodule generated by {0}".format(self._basis_all)173174def __iter__(self):175"""176EXAMPLE::177178sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator179sage: F.<x,y,z> = FreeAlgebra(GF(3),3)180sage: list(FiniteZZsubmodule_iterator([x,y], [3,3])) #indirect doctest181[0, x, 2*x, y, x + y, 2*x + y, 2*y, x + 2*y, 2*x + 2*y]182"""183return self184185cdef ModuleElement _iteration(FiniteZZsubmodule_iterator self):186"""187This is the core implementation of the iteration.188189EXAMPLES::190191sage: from sage.modules.finite_submodule_iter import FiniteZZsubmodule_iterator192sage: F.<x,y,z> = FreeAlgebra(GF(3),3)193sage: iter = FiniteZZsubmodule_iterator([x,y], [3,3])194sage: next(iter) #indirect doctest1950196sage: print next(iter), next(iter), next(iter) #indirect doctest197x 2*x y198"""199if self._basis_length == 1:200if self._count < self._order:201self._cw = self._plus[self._count]202self._count += 1203else:204raise StopIteration205else:206if self._count == 0 or self._count == self._order:207self._other = self._other_ZZ.next()208self._cw = < ModuleElement > self._other209self._count = 1210else:211self._cw = self._other + self._plus[self._count]212self._count += 1213214215cdef class FiniteFieldsubspace_iterator(FiniteZZsubmodule_iterator):216"""217This class implements an iterator over the subspace of a vector218space over a finite field. The subspace is generated by ``basis``.219220INPUT:221222- ``basis`` -- a list of vectors or a matrix with elements over223a finite field. If a matrix is provided then it is not checked224whether the matrix is full ranked. Similarly, if a list of225vectors is provided, then the linear independence of the vectors226is not checked.227228- ``coset_rep`` (optional) -- a vector in the same ambient space,229if one aims to compute a coset of the vector space given by ``basis``.230231EXAMPLES::232233sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator234sage: A = random_matrix(GF(2), 10, 100)235sage: iter = FiniteFieldsubspace_iterator(A)236sage: len(list(iter))2371024238sage: X = random_matrix(GF(4, 'a'), 7, 100).row_space()239sage: s = list(X) # long time (5s on sage.math, 2013)240sage: t = list(FiniteFieldsubspace_iterator(X.basis())) # takes 0.31s241sage: sorted(t) == sorted(s) # long time242True243"""244245def __init__(self, basis, coset_rep=None):246"""247see :class:`FiniteFieldsubspace_iterator`248249EXAMPLES::250251sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator252sage: A = random_matrix(GF(2), 10, 100)253sage: iter = FiniteFieldsubspace_iterator(A)254sage: X = list(iter)255sage: len(X)2561024257sage: v = random_vector(GF(2), 100)258sage: iter = FiniteFieldsubspace_iterator(A, v)259sage: Y = list(iter)260sage: len(Y)2611024262sage: all([Y[i]-X[i]==v for i in range(len(X))])263True264"""265cdef Py_ssize_t d, i, p266cdef list pows, order267268F = basis[0].base_ring()269P = F.prime_subfield()270p = P.order()271alpha = F.primitive_element()272degree = F.degree()273274pows = [alpha ** i for i in range(degree)]275basis = [_p * x for x in basis for _p in pows] # a ZZ_p-basis for the vectorspace276order = [p] * (len(basis))277278FiniteZZsubmodule_iterator.__init__(self, basis, order, coset_rep)279280cdef class FiniteFieldsubspace_projPoint_iterator:281"""282This class implements an iterator over the projective points of a vector283space over a finite field. The vector space is generated by ``basis`` and284need not to be equal to the full ambient space.285286A projective point (= one dimensional subspace) `P` will be represented by a287generator `p`. To ensure that all `p` will be normalized you can set the288optional argument ``normalize`` to ``True``.289290INPUT:291292- ``basis`` -- a list of vectors or a matrix with elements over293a finite field. If a matrix is provided then it is not checked294whether the matrix is full ranked. Similarly, if a list of295vectors is provided, then the linear independence of the vectors296is not checked.297298- ``normalize`` (optional) -- boolean which indicates if the299returned vectors should be normalized, i.e. the first nonzero coordinate300is equal to 1. Default: False301302EXAMPLES::303304sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_iterator, FiniteFieldsubspace_projPoint_iterator305sage: A = random_matrix(GF(4, 'a'), 5, 100)306sage: a = len(list(FiniteFieldsubspace_iterator(A)))307sage: b = len(list(FiniteFieldsubspace_projPoint_iterator(A)))308sage: b == (a-1)/3309True310311Prove that the option ``normalize == True`` will only return normalized vectors.312313sage: all([ x.monic() == x for x in FiniteFieldsubspace_projPoint_iterator(A, True) ])314True315316TESTS::317318sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator319sage: A = MatrixSpace(GF(7), 10, 10).one()320sage: len(list(FiniteFieldsubspace_projPoint_iterator(A[:0]))) #indirect doctest3210322sage: len(list(FiniteFieldsubspace_projPoint_iterator(A[:1]))) #indirect doctest3231324sage: len(list(FiniteFieldsubspace_projPoint_iterator(A[:2]))) #indirect doctest3258326"""327328def __init__(self, basis, normalize=False):329"""330see :class:`FiniteFieldsubspace_projPoint_iterator`331332EXAMPLES::333334sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator335sage: A = random_matrix(GF(4, 'a'), 4, 100)336sage: iter = FiniteFieldsubspace_projPoint_iterator(A)337sage: len(list(iter))33885339"""340from sage.matrix.constructor import matrix341cdef i342self._basis = list(basis)343self._basis_length = len(self._basis)344if normalize:345B = matrix(self._basis)346B.echelonize()347self._basis = B.rows()348self._basis.reverse()349350if self._basis_length == 0:351self._one_dimensional_case = 2352else:353self._one_dimensional_case = 1354355def __next__(self):356"""357Return the next projective point. This will just add/subtract358another element of the ``basis`` except for the cases when the rank will359increase.360361EXAMPLES::362363sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator364sage: A = MatrixSpace(GF(3), 10,10).one()365sage: iter = FiniteFieldsubspace_projPoint_iterator(A)366sage: next(iter) #indirect doctest367(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)368sage: next(iter) #indirect doctest369(0, 1, 0, 0, 0, 0, 0, 0, 0, 0)370"""371if self._one_dimensional_case > 0:372if self._one_dimensional_case == 1:373self._one_dimensional_case = 2374return self._basis[0]375else:376if self._basis_length > 1:377self._it = FiniteFieldsubspace_iterator(self._basis[:1], self._basis[1])378self._normalized_pos = 1379self._one_dimensional_case = 0380else:381raise StopIteration382try:383return self._it.next()384except StopIteration:385self._normalized_pos += 1386if self._normalized_pos == self._basis_length:387raise StopIteration388else:389self._it = FiniteFieldsubspace_iterator(self._basis[:self._normalized_pos], self._basis[self._normalized_pos])390return self._it.next()391392def __repr__(self):393"""394EXAMPLES::395396sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator397sage: A = MatrixSpace(GF(3), 2, 2).one()398sage: FiniteFieldsubspace_projPoint_iterator(A)399Iterator over the projective points of a subspace generated by [(1, 0), (0, 1)]400"""401402return "Iterator over the projective points of a subspace generated by {0}".format(self._basis)403404def __iter__(self):405"""406EXAMPLE::407408sage: from sage.modules.finite_submodule_iter import FiniteFieldsubspace_projPoint_iterator409sage: A = MatrixSpace(GF(3), 10,10).one()410sage: len(list(FiniteFieldsubspace_projPoint_iterator(A))) #indirect doctest41129524412sage: A = MatrixSpace(GF(3), 1,1).one()413sage: len(list(FiniteFieldsubspace_projPoint_iterator(A))) #indirect doctest4141415"""416return self417418419