def test_random_prime_fields(self, num=16): n = 7 k = 3 for i in range(num): o = random_prime(2**128, lbound=n) s = randint(0, o - 1) assert s == templ_generic(7, 3, o, s)
def _an_element_(self): r""" Return an element of the spectrum of the ring. OUTPUT: A point of the affine scheme ``self``. EXAMPLES:: sage: Spec(QQ).an_element() Point on Spectrum of Rational Field defined by the Principal ideal (0) of Rational Field sage: Spec(ZZ).an_element() # random output Point on Spectrum of Integer Ring defined by the Principal ideal (811) of Integer Ring """ if self.coordinate_ring() is ZZ: from sage.rings.arith import random_prime return self(random_prime(1000)) return self(0)
def _an_element_(self): r""" Return an element of the spectrum of the ring. OUTPUT: A point of the affine scheme ``self``. EXAMPLES:: sage: Spec(QQ).an_element() Point on Spectrum of Rational Field defined by the Principal ideal (0) of Rational Field sage: Spec(ZZ).an_element() # random output Point on Spectrum of Integer Ring defined by the Principal ideal (811) of Integer Ring """ if self.coordinate_ring() is ZZ: from sage.rings.arith import random_prime return self(ZZ.ideal(random_prime(1000))) return self(self.coordinate_ring().zero_ideal())
def random_blum_prime(lbound, ubound, ntries=100): r""" A random Blum prime within the specified bounds. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`. INPUT: - ``lbound`` -- positive integer; the lower bound on how small a random Blum prime `p` can be. So we have ``0 < lbound <= p <= ubound``. The lower bound must be distinct from the upper bound. - ``ubound`` -- positive integer; the upper bound on how large a random Blum prime `p` can be. So we have ``0 < lbound <= p <= ubound``. The lower bound must be distinct from the upper bound. - ``ntries`` -- (default: ``100``) the number of attempts to generate a random Blum prime. If ``ntries`` is a positive integer, then perform that many attempts at generating a random Blum prime. This might or might not result in a Blum prime. OUTPUT: - A random Blum prime within the specified lower and upper bounds. .. NOTE:: Beware that there might not be any primes between the lower and upper bounds. So make sure that these two bounds are "sufficiently" far apart from each other for there to be primes congruent to 3 modulo 4. In particular, there should be at least two distinct Blum primes within the specified bounds. EXAMPLES: Choose a random prime and check that it is a Blum prime:: sage: from sage.crypto.util import random_blum_prime sage: p = random_blum_prime(10**4, 10**5) sage: is_prime(p) True sage: mod(p, 4) == 3 True TESTS: Make sure that there is at least one Blum prime between the lower and upper bounds. In the following example, we have ``lbound=24`` and ``ubound=30`` with 29 being the only prime within those bounds. But 29 is not a Blum prime. :: sage: from sage.crypto.util import random_blum_prime sage: random_blum_prime(24, 30, ntries=10) Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval. sage: random_blum_prime(24, 28) Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval. """ # sanity check if not has_blum_prime(lbound, ubound): raise ValueError("No Blum primes within the specified closed interval.") # Now we know that there is a Blum prime within the closed interval # [lbound, ubound]. Pick one such prime at random. p = random_prime(ubound, lbound=lbound, proof=True) n = 1 while mod(p, 4) != 3: p = random_prime(ubound, lbound=lbound, proof=True) n += 1 if n > ntries: raise ValueError("Maximum number of attempts exceeded.") return p
def spanning_maass_products(ring, weight, subspaces=None, lazy_rank_check=False): r"""This function is intended to quickly find good generators. This may be calculated in low precision and then ported to higher. the maass spaces of all subspaces are expected to be ordered the same way as ``ring.type()._maass_generator_preimages``. """ ## Outline : ## - get the basis as polynomials form the ring ## - get the maass products, convert them to polynomials and try to ## construct a basis ## - get the maass space associated to this weight and get its coordinates ## - return a basis containing of maass forms and the products if subspaces is None: subspaces = dict() assert ring.base_ring() is QQ or ring.base_ring() is ZZ, \ "%s doesn't have rational base field" % ring dim = ring.graded_submodule(weight).dimension() precision = ring.fourier_expansion_precision() maass_lift_func = ring.type()._maass_generators maass_lift_preimage_func = ring.type()._maass_generator_preimages lift_polys = [] preims = [] monomials = set() for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 4), 3, -2): if k not in subspaces: subspaces[k] = ring.graded_submodule(k) if weight - k not in subspaces: subspaces[weight - k] = ring.graded_submodule(weight - k) try: kgs = zip(subspaces[k].maass_space()._provided_basis(), maass_lift_preimage_func(k)) ogs = zip(subspaces[weight - k].maass_space()._provided_basis(), maass_lift_preimage_func(weight - k)) except ValueError: continue for f_coords, f_pre in kgs: for g_coords, g_pre in ogs: assert f_coords.parent().graded_ambient() is ring assert g_coords.parent().graded_ambient() is ring f = ring(f_coords) g = ring(g_coords) fg_poly = (f * g)._reduce_polynomial() monomials = monomials.union(set(fg_poly.monomials())) M = matrix(QQ, len(lift_polys) + 1, [ poly.monomial_coefficient(m) for poly in lift_polys + [fg_poly] for m in monomials ]) # TODO : Use linbox try: if magma(M).Rank() <= len(lift_polys): continue except TypeError: for i in xrange(10): Mp = matrix(Qp(random_prime(10**10), 10), M) if Mp.rank() > len(lift_polys): break else: if lazy_rank_check: continue elif M.rank() <= len(lift_polys): continue lift_polys.append(fg_poly) preims.append([f_pre, g_pre]) if len(lift_polys) == dim: break if len(lift_polys) == dim: ss = construct_from_maass_products(ring, weight, zip(preims, lift_polys), True, False, True) poly_coords = [[0] * i + [1] + (len(lift_polys) - i - 1) * [0] for i in xrange(len(lift_polys))] else: # The products of two Maass lifts don't span this space # we hence have to consider the Maass lifts ss = ring.graded_submodule(weight) poly_coords = [ss.coordinates(ring(p)) for p in lift_polys] maass_lifts = maass_lift_func(weight, precision) preims[0:0] = [[p] for p in maass_lift_preimage_func(weight)] all_coords = map(ss.coordinates, maass_lifts) + poly_coords M = matrix(QQ, all_coords).transpose() nmb_mls = len(maass_lifts) for i in xrange(10): Mp = matrix(Qp(random_prime(10**10), 10), M) pvs = Mp.pivots() if pvs[:nmb_mls] == range(nmb_mls) and \ len(pvs) == dim : break else: pvs = M.pivots() if len(pvs) == dim: return [(preims[i], ring(ss(all_coords[i])).polynomial()) for i in pvs] else: raise RuntimeError, "The products of at most two Maass lifts don't span " + \ "this space"
def gritsenko_products(N, weight, dimension, precision = None) : """ INPUT: - `N` -- the level of the paramodular group. - ``weight`` -- the weight of the space, that we consider. - ``precision`` -- the precision to be used for the underlying Fourier expansions. """ min_precision = _minimal_paramodular_precision(N, weight) if precision is None : precision = min_precision else : precision = max(precision, min_precision) # - Lifts betrachten # - Zerlegung des Gewichts benutzen, dort Lifts nach und nach # multiplizieren, um jeweils den Rang der unterliegenden Matrix # zu bestimmen. Dabei mod p mit zufaelligem p arbeiten fourier_expansions = list(gritsenko_lift_subspace(N, weight, precision)) products = [ [(weight, i)] for i in xrange(len(fourier_expansions)) ] fourier_indices = list(precision) fourier_matrix = matrix(ZZ, [ [e[k] for k in fourier_indices] for e in fourier_expansions] ) for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 2), 1, -2) : space_k = list(enumerate(gritsenko_lift_subspace(N, k, precision))) space_wk = list(enumerate(gritsenko_lift_subspace(N, weight - k, precision))) if len(space_wk) == 0 : continue for (i,f) in space_k : for (j,g) in space_wk : if fourier_matrix.nrows() == dimension : return products, fourier_expansions cur_fe = f * g # We are using lazy rank checks for p in [random_prime(10**10) for _ in range(2)] : fourier_matrix_cur = matrix(GF(p), fourier_matrix.nrows() + 1, fourier_matrix.ncols()) fourier_matrix_cur.set_block(0, 0, fourier_matrix) fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0, matrix(GF(p), 1, [cur_fe[fi] for fi in fourier_indices]) ) if fourier_matrix_cur.rank() == fourier_matrix_cur.nrows() : break else : continue products.append( [(k, i), (weight - k, j)] ) fourier_expansions.append(cur_fe) fourier_matrix_cur = matrix( ZZ, fourier_matrix.nrows() + 1, fourier_matrix.ncols() ) fourier_matrix_cur.set_block(0, 0, fourier_matrix) fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0, matrix(ZZ, 1, [cur_fe[fi] for fi in fourier_indices]) ) fourier_matrix = fourier_matrix_cur return products, fourier_expansions
def construct_from_maass_products(ring, weight, products, is_basis=True, provides_maass_spezialschar=False, is_integral=False, lazy_rank_check=True): r""" Pass the return value of spanning_maass_products of a space of Siegel modular forms of same type. This will return a space using these forms. Whenever ``is_basis`` is False the the products are first filtered to yield a basis. """ assert QQ.has_coerce_map_from(ring.base_ring()) or ring.base_ring() is ZZ, \ "%s doesn't have rational base field" % ring dim = ring.graded_submodule(weight).dimension() ## if the products don't provide a basis, we have to choose one if not is_basis: ## we prefer to use Maass lifts, since they are very cheap maass_forms = [] non_maass_forms = [] for p in products: if len(p[0]) == 1: maass_forms.append(p) else: non_maass_forms.append(p) monomials = set() lift_polys = [] products = [] for lifts, lift_poly in maass_forms + non_maass_forms: red_poly = ring( ring.relations().ring()(lift_poly))._reduce_polynomial() monomials = monomials.union(set(red_poly.monomials())) M = matrix(QQ, len(lift_polys) + 1, [ poly.monomial_coefficient(m) for poly in lift_polys + [lift_poly] for m in monomials ]) # TODO : Use linbox try: if magma(M).Rank() > len(lift_polys): break except TypeError: for i in xrange(10): Mp = matrix(Qp(random_prime(10**10), 10), M) if Mp.rank() > len(lift_polys): break else: if lazy_rank_check: continue elif M.rank() <= len(lift_polys): continue lift_polys.append(red_poly) products.append((lifts, red_poly)) if len(products) == dim: break else: raise ValueError, "products don't provide a basis" basis = [] if provides_maass_spezialschar: maass_form_indices = [] for i, (lifts, lift_poly) in enumerate(products): e = ring(ring.relations().ring()(lift_poly)) if len(lifts) == 1: l = lifts[0] e._set_fourier_expansion( SiegelModularFormG2MaassLift( l[0], l[1], ring.fourier_expansion_precision(), is_integral=is_integral)) elif len(lifts) == 2: (l0, l1) = tuple(lifts) e._set_fourier_expansion( EquivariantMonoidPowerSeries_LazyMultiplication( SiegelModularFormG2MaassLift( l0[0], l0[1], ring.fourier_expansion_precision(), is_integral=is_integral), SiegelModularFormG2MaassLift( l1[0], l1[1], ring.fourier_expansion_precision(), is_integral=is_integral))) else: e._set_fourier_expansion( prod( SiegelModularFormG2MaassLift( l[0], l[1], ring.precision(), is_integral=is_integral) for l in lifts)) basis.append(e) if provides_maass_spezialschar and len(lifts) == 1: maass_form_indices.append(i) ss = ring._submodule(basis, grading_indices=(weight, ), is_heckeinvariant=True) if provides_maass_spezialschar: maass_coords = [ ss([0] * i + [1] + [0] * (dim - i - 1)) for i in maass_form_indices ] ss.maass_space.set_cache( SiegelModularFormG2Submodule_maassspace(ss, map(ss, maass_coords))) return ss
def spanning_maass_products(ring, weight, subspaces=None, lazy_rank_check=False): r"""This function is intended to quickly find good generators. This may be calculated in low precision and then ported to higher. the maass spaces of all subspaces are expected to be ordered the same way as ``ring.type()._maass_generator_preimages``. """ ## Outline : ## - get the basis as polynomials form the ring ## - get the maass products, convert them to polynomials and try to ## construct a basis ## - get the maass space associated to this weight and get its coordinates ## - return a basis containing of maass forms and the products if subspaces is None: subspaces = dict() assert ring.base_ring() is QQ or ring.base_ring() is ZZ, "%s doesn't have rational base field" % ring dim = ring.graded_submodule(weight).dimension() precision = ring.fourier_expansion_precision() maass_lift_func = ring.type()._maass_generators maass_lift_preimage_func = ring.type()._maass_generator_preimages lift_polys = [] preims = [] monomials = set() for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 4), 3, -2): if k not in subspaces: subspaces[k] = ring.graded_submodule(k) if weight - k not in subspaces: subspaces[weight - k] = ring.graded_submodule(weight - k) try: kgs = zip(subspaces[k].maass_space()._provided_basis(), maass_lift_preimage_func(k)) ogs = zip(subspaces[weight - k].maass_space()._provided_basis(), maass_lift_preimage_func(weight - k)) except ValueError: continue for f_coords, f_pre in kgs: for g_coords, g_pre in ogs: assert f_coords.parent().graded_ambient() is ring assert g_coords.parent().graded_ambient() is ring f = ring(f_coords) g = ring(g_coords) fg_poly = (f * g)._reduce_polynomial() monomials = monomials.union(set(fg_poly.monomials())) M = matrix( QQ, len(lift_polys) + 1, [poly.monomial_coefficient(m) for poly in lift_polys + [fg_poly] for m in monomials], ) # TODO : Use linbox try: if magma(M).Rank() <= len(lift_polys): continue except TypeError: for i in xrange(10): Mp = matrix(Qp(random_prime(10 ** 10), 10), M) if Mp.rank() > len(lift_polys): break else: if lazy_rank_check: continue elif M.rank() <= len(lift_polys): continue lift_polys.append(fg_poly) preims.append([f_pre, g_pre]) if len(lift_polys) == dim: break if len(lift_polys) == dim: ss = construct_from_maass_products(ring, weight, zip(preims, lift_polys), True, False, True) poly_coords = [[0] * i + [1] + (len(lift_polys) - i - 1) * [0] for i in xrange(len(lift_polys))] else: # The products of two Maass lifts don't span this space # we hence have to consider the Maass lifts ss = ring.graded_submodule(weight) poly_coords = [ss.coordinates(ring(p)) for p in lift_polys] maass_lifts = maass_lift_func(weight, precision) preims[0:0] = [[p] for p in maass_lift_preimage_func(weight)] all_coords = map(ss.coordinates, maass_lifts) + poly_coords M = matrix(QQ, all_coords).transpose() nmb_mls = len(maass_lifts) for i in xrange(10): Mp = matrix(Qp(random_prime(10 ** 10), 10), M) pvs = Mp.pivots() if pvs[:nmb_mls] == range(nmb_mls) and len(pvs) == dim: break else: pvs = M.pivots() if len(pvs) == dim: return [(preims[i], ring(ss(all_coords[i])).polynomial()) for i in pvs] else: raise RuntimeError, "The products of at most two Maass lifts don't span " + "this space"
def construct_from_maass_products( ring, weight, products, is_basis=True, provides_maass_spezialschar=False, is_integral=False, lazy_rank_check=True ): r""" Pass the return value of spanning_maass_products of a space of Siegel modular forms of same type. This will return a space using these forms. Whenever ``is_basis`` is False the the products are first filtered to yield a basis. """ assert QQ.has_coerce_map_from(ring.base_ring()) or ring.base_ring() is ZZ, ( "%s doesn't have rational base field" % ring ) dim = ring.graded_submodule(weight).dimension() ## if the products don't provide a basis, we have to choose one if not is_basis: ## we prefer to use Maass lifts, since they are very cheap maass_forms = [] non_maass_forms = [] for p in products: if len(p[0]) == 1: maass_forms.append(p) else: non_maass_forms.append(p) monomials = set() lift_polys = [] products = [] for lifts, lift_poly in maass_forms + non_maass_forms: red_poly = ring(ring.relations().ring()(lift_poly))._reduce_polynomial() monomials = monomials.union(set(red_poly.monomials())) M = matrix( QQ, len(lift_polys) + 1, [poly.monomial_coefficient(m) for poly in lift_polys + [lift_poly] for m in monomials], ) # TODO : Use linbox try: if magma(M).Rank() > len(lift_polys): break except TypeError: for i in xrange(10): Mp = matrix(Qp(random_prime(10 ** 10), 10), M) if Mp.rank() > len(lift_polys): break else: if lazy_rank_check: continue elif M.rank() <= len(lift_polys): continue lift_polys.append(red_poly) products.append((lifts, red_poly)) if len(products) == dim: break else: raise ValueError, "products don't provide a basis" basis = [] if provides_maass_spezialschar: maass_form_indices = [] for i, (lifts, lift_poly) in enumerate(products): e = ring(ring.relations().ring()(lift_poly)) if len(lifts) == 1: l = lifts[0] e._set_fourier_expansion( SiegelModularFormG2MaassLift(l[0], l[1], ring.fourier_expansion_precision(), is_integral=is_integral) ) elif len(lifts) == 2: (l0, l1) = tuple(lifts) e._set_fourier_expansion( EquivariantMonoidPowerSeries_LazyMultiplication( SiegelModularFormG2MaassLift( l0[0], l0[1], ring.fourier_expansion_precision(), is_integral=is_integral ), SiegelModularFormG2MaassLift( l1[0], l1[1], ring.fourier_expansion_precision(), is_integral=is_integral ), ) ) else: e._set_fourier_expansion( prod(SiegelModularFormG2MaassLift(l[0], l[1], ring.precision(), is_integral=is_integral) for l in lifts) ) basis.append(e) if provides_maass_spezialschar and len(lifts) == 1: maass_form_indices.append(i) ss = ring._submodule(basis, grading_indices=(weight,), is_heckeinvariant=True) if provides_maass_spezialschar: maass_coords = [ss([0] * i + [1] + [0] * (dim - i - 1)) for i in maass_form_indices] ss.maass_space.set_cache(SiegelModularFormG2Submodule_maassspace(ss, map(ss, maass_coords))) return ss
def test_correctness_and_precision_of_solve_diff_eqn(number=20, verbosity=1): """ ``number`` is how many different random distributions to check. Currently, avoids the prime 2. """ from sage.misc.prandom import randint from sage.rings.arith import random_prime from sage.rings.padics.factory import ZpCA from sage.modular.pollack_stevens.coeffmod_OMS_space import OverconvergentDistributions from sage.structure.sage_object import dumps errors = [] munus = [] for i in range(number): Mspace = randint(1, 20) #Moments of space M = randint(max(0, Mspace - 5), Mspace) p = random_prime(13, lbound=3) k = randint(0, 6) Rprec = Mspace + randint(0, 5) R = ZpCA(p, Rprec) D = OverconvergentDistributions(k, base=R, prec_cap=Mspace) S0 = D.action().actor() Delta_mat = S0([1,1,0,1]) mu = D.random_element(M) mu_save = dumps(mu)#[deepcopy(mu.ordp), deepcopy(mu._moments)] if verbosity > 0: print "\nTest #{0} data (Mspace, M, p, k, Rprec) =".format(i+1), (Mspace, M, p, k, Rprec) print "mu =", mu nu = mu * Delta_mat - mu nu_save = [deepcopy(nu.ordp), deepcopy(nu._moments)] mu2 = nu.solve_diff_eqn() nu_abs_prec = nu.precision_absolute() expected = nu_abs_prec - nu_abs_prec.exact_log(p) - 1 if M != 1: try: agree = (mu - mu2).is_zero(expected) except PrecisionError: print (Mspace, M, p, k, Rprec), mu_save._repr_(), nu_save assert False else: agree = mu2.is_zero(expected) if verbosity > 1: print " Just so you know:" print " mured =", mu.reduce_precision_absolute(expected) print " mu2 =", mu2 print " nu = ", nu if not agree: errors.append((i+1, 1)) munus.append((mu_save, nu_save, mu2, (Mspace, M, p, k, Rprec))) if verbosity > 0: print " Test finding mu from mu|Delta accurate: %s"%(agree) print " nu_abs_prec soln_abs_prec_expected actual agree" mu2_abs_prec = mu2.precision_absolute() agree = (expected == mu2_abs_prec) if verbosity > 0: print " %s %s %s %s"%(nu_abs_prec, expected, mu2_abs_prec, agree) if not agree: errors.append((i+1, 2)) munus.append((mu_save, nu_save, mu2, (Mspace, M, p, k, Rprec))) if mu.precision_relative() > 0: mu._moments[0] = R(0, mu.precision_relative()) mu_save = [deepcopy(mu.ordp), deepcopy(mu._moments)] if verbosity > 0: print " mu modified =", mu nu = mu.solve_diff_eqn() mu_abs_prec = mu.precision_absolute() expected = mu_abs_prec - mu_abs_prec.exact_log(p) - 1 nud = nu * Delta_mat - nu nu_save = [deepcopy(nu.ordp), deepcopy(nu._moments)] agree = (nud - mu).is_zero(expected) if verbosity > 1: print " Just so you know:" print " mu =", mu print " mured =", mu.reduce_precision_absolute(expected) print " nud =", nud if not agree: errors.append((i+1, 3)) munus.append((mu_save, nu_save, (Mspace, M, p, k, Rprec))) if verbosity > 0: print " Test finding nu with nu|Delta == mu: %s"%(agree) print " mu_abs_prec soln_abs_prec_expected actual agree" nu_abs_prec = nu.precision_absolute() agree = (expected == nu_abs_prec) if verbosity > 0: print " %s %s %s %s"%(mu_abs_prec, expected, nu_abs_prec, agree) if not agree: errors.append((i+1, 4)) munus.append((mu_save, nu_save, (Mspace, M, p, k, Rprec))) if len(errors) == 0: if verbosity > 0: print "\nTest passed with no errors." return if verbosity > 0: print "\nTest failed with errors: %s\n"%(errors) return errors, munus
def gritsenko_products(N, weight, dimension, precision=None): """ INPUT: - `N` -- the level of the paramodular group. - ``weight`` -- the weight of the space, that we consider. - ``precision`` -- the precision to be used for the underlying Fourier expansions. """ min_precision = _minimal_paramodular_precision(N, weight) if precision is None: precision = min_precision else: precision = max(precision, min_precision) # - Lifts betrachten # - Zerlegung des Gewichts benutzen, dort Lifts nach und nach # multiplizieren, um jeweils den Rang der unterliegenden Matrix # zu bestimmen. Dabei mod p mit zufaelligem p arbeiten fourier_expansions = list(gritsenko_lift_subspace(N, weight, precision)) products = [[(weight, i)] for i in xrange(len(fourier_expansions))] fourier_indices = list(precision) fourier_matrix = matrix(ZZ, [[e[k] for k in fourier_indices] for e in fourier_expansions]) for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 2), 1, -2): space_k = list(enumerate(gritsenko_lift_subspace(N, k, precision))) space_wk = list( enumerate(gritsenko_lift_subspace(N, weight - k, precision))) if len(space_wk) == 0: continue for (i, f) in space_k: for (j, g) in space_wk: if fourier_matrix.nrows() == dimension: return products, fourier_expansions cur_fe = f * g # We are using lazy rank checks for p in [random_prime(10**10) for _ in range(2)]: fourier_matrix_cur = matrix(GF(p), fourier_matrix.nrows() + 1, fourier_matrix.ncols()) fourier_matrix_cur.set_block(0, 0, fourier_matrix) fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0, matrix(GF(p), 1, [cur_fe[fi] for fi in fourier_indices])) if fourier_matrix_cur.rank() == fourier_matrix_cur.nrows(): break else: continue products.append([(k, i), (weight - k, j)]) fourier_expansions.append(cur_fe) fourier_matrix_cur = matrix(ZZ, fourier_matrix.nrows() + 1, fourier_matrix.ncols()) fourier_matrix_cur.set_block(0, 0, fourier_matrix) fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0, matrix(ZZ, 1, [cur_fe[fi] for fi in fourier_indices])) fourier_matrix = fourier_matrix_cur return products, fourier_expansions