Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/quadratic_forms/count_local_2.pyx
4078 views
1
r"""
2
Optimised Cython code for counting congruence solutions
3
"""
4
5
include "../ext/cdefs.pxi"
6
include "../ext/gmp.pxi"
7
8
from sage.rings.arith import valuation, kronecker_symbol, is_prime
9
from sage.rings.finite_rings.integer_mod import IntegerMod, Mod
10
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
11
12
from sage.rings.integer_ring import ZZ
13
14
from sage.rings.finite_rings.integer_mod cimport IntegerMod_gmp
15
from sage.sets.set import Set
16
17
18
19
20
def extract_sublist_indices(Biglist, Smalllist):
21
"""
22
Returns the indices of Biglist which index the entries of
23
Smalllist appearing in Biglist. (Note that Smalllist may not be a
24
sublist of Biglist.)
25
26
NOTE 1: This is an internal routine which deals with re-indexing
27
lists, and is not exported to the QuadraticForm namespace!
28
29
NOTE 2: This should really by applied only when BigList has no
30
repeated entries.
31
32
TO DO: *** Please revisit this routine, and eliminate it! ***
33
34
INPUT:
35
Biglist, Smalllist -- two lists of a common type, where Biglist has no
36
repeated entries.
37
38
OUTPUT:
39
a list of integers >= 0
40
41
EXAMPLES::
42
43
sage: from sage.quadratic_forms.count_local_2 import extract_sublist_indices
44
45
sage: biglist = [1,3,5,7,8,2,4]
46
sage: sublist = [5,3,2]
47
sage: sublist == [biglist[i] for i in extract_sublist_indices(biglist, sublist)] ## Ok whenever Smalllist is a sublist of Biglist
48
True
49
50
sage: extract_sublist_indices([1,2,3,6,9,11], [1,3,2,9])
51
[0, 2, 1, 4]
52
53
sage: extract_sublist_indices([1,2,3,6,9,11], [1,3,10,2,9,0])
54
[0, 2, 1, 4]
55
56
sage: extract_sublist_indices([1,3,5,3,8], [1,5])
57
Traceback (most recent call last):
58
...
59
TypeError: Biglist must not have repeated entries!
60
"""
61
## Check that Biglist has no repeated entries
62
Big_set = Set(Biglist)
63
if len(Set(Biglist)) != len(Biglist):
64
raise TypeError, "Biglist must not have repeated entries!"
65
66
## Extract the indices of Biglist needed to make Sublist
67
index_list = []
68
for x in Smalllist:
69
try:
70
index_list.append(Biglist.index(x))
71
except ValueError: ## This happens when an entry of Smalllist is not contained in Biglist
72
None
73
74
## Return the list if indices
75
return index_list
76
77
78
79
80
def count_modp__by_gauss_sum(n, p, m, Qdet):
81
"""
82
Returns the number of solutions of Q(x) = m over the finite field
83
Z/pZ, where p is a prime number > 2 and Q is a non-degenerate
84
quadratic form of dimension n >= 1 and has Gram determinant Qdet.
85
86
REFERENCE:
87
These are defined in Table 1 on p363 of Hanke's "Local
88
Densities..." paper.
89
90
INPUT:
91
92
- n -- an integer >= 1
93
- p -- a prime number > 2
94
- m -- an integer
95
- Qdet -- a integer which is non-zero mod p
96
97
OUTPUT:
98
an integer >= 0
99
100
EXAMPLES::
101
102
sage: from sage.quadratic_forms.count_local_2 import count_modp__by_gauss_sum
103
104
sage: count_modp__by_gauss_sum(3, 3, 0, 1) ## for Q = x^2 + y^2 + z^2 => Gram Det = 1 (mod 3)
105
9
106
sage: count_modp__by_gauss_sum(3, 3, 1, 1) ## for Q = x^2 + y^2 + z^2 => Gram Det = 1 (mod 3)
107
6
108
sage: count_modp__by_gauss_sum(3, 3, 2, 1) ## for Q = x^2 + y^2 + z^2 => Gram Det = 1 (mod 3)
109
12
110
111
sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1])
112
sage: [Q.count_congruence_solutions(3, 1, m, None, None) == count_modp__by_gauss_sum(3, 3, m, 1) for m in range(3)]
113
[True, True, True]
114
115
116
sage: count_modp__by_gauss_sum(3, 3, 0, 2) ## for Q = x^2 + y^2 + 2*z^2 => Gram Det = 2 (mod 3)
117
9
118
sage: count_modp__by_gauss_sum(3, 3, 1, 2) ## for Q = x^2 + y^2 + 2*z^2 => Gram Det = 2 (mod 3)
119
12
120
sage: count_modp__by_gauss_sum(3, 3, 2, 2) ## for Q = x^2 + y^2 + 2*z^2 => Gram Det = 2 (mod 3)
121
6
122
123
sage: Q = DiagonalQuadraticForm(ZZ, [1,1,2])
124
sage: [Q.count_congruence_solutions(3, 1, m, None, None) == count_modp__by_gauss_sum(3, 3, m, 2) for m in range(3)]
125
[True, True, True]
126
127
128
"""
129
## Check that Qdet is non-degenerate
130
if Qdet % p == 0:
131
raise RuntimeError, "Qdet must be non-zero."
132
133
## Check that p is prime > 0
134
if not is_prime(p) or p == 2:
135
raise RuntimeError, "p must be a prime number > 2."
136
137
## Check that n >= 1
138
if n < 1:
139
raise RuntimeError, "the dimension n must be >= 1."
140
141
## Compute the Gauss sum
142
neg1 = -1
143
if (m % p == 0):
144
if (n % 2 != 0):
145
count = (p**(n-1))
146
else:
147
count = (p**(n-1)) + (p-1) * (p**((n-2)/2)) * kronecker_symbol(((neg1**(n/2)) * Qdet) % p, p)
148
else:
149
if (n % 2 != 0):
150
count = (p**(n-1)) + (p**((n-1)/2)) * kronecker_symbol(((neg1**((n-1)/2)) * Qdet * m) % p, p)
151
else:
152
count = (p**(n-1)) - (p**((n-2)/2)) * kronecker_symbol(((neg1**(n/2)) * Qdet) % p, p)
153
154
## Return the result
155
return count
156
157
158
159
160
161
162
cdef CountAllLocalTypesNaive_cdef(Q, p, k, m, zvec, nzvec):
163
"""
164
This Cython routine is documented in its Python wrapper method
165
QuadraticForm.count_congruence_solutions_by_type().
166
"""
167
cdef long n, i
168
cdef long a, b ## Used to quickly evaluate Q(v)
169
cdef long ptr ## Used to increment the vector
170
cdef long solntype ## Used to store the kind of solution we find
171
172
173
## Some shortcuts and definitions
174
n = Q.dim()
175
R = p ** k
176
Q1 = Q.base_change_to(IntegerModRing(R))
177
178
179
## Cython Variables
180
cdef IntegerMod_gmp zero, one
181
zero = IntegerMod_gmp(IntegerModRing(R), 0)
182
one = IntegerMod_gmp(IntegerModRing(R), 1)
183
184
185
186
## Initialize the counting vector
187
count_vector = [0 for i in range(6)]
188
189
## Initialize v = (0, ... , 0)
190
v = [Mod(0, R) for i in range(n)]
191
192
193
## Some declarations to speed up the loop
194
R_n = R ** n
195
m1 = Mod(m, R)
196
197
## Count the local solutions
198
for i from 0 <= i < R_n:
199
200
## Perform a carry (when value = R-1) until we can increment freely
201
ptr = len(v)
202
while ((ptr > 0) and (v[ptr-1] == R-1)):
203
v[ptr-1] += 1
204
ptr += -1
205
206
## Only increment if we're not already at the zero vector =)
207
if (ptr > 0):
208
v[ptr-1] += 1
209
210
211
## Evaluate Q(v) quickly
212
tmp_val = Mod(0, R)
213
for a from 0 <= a < n:
214
for b from a <= b < n:
215
tmp_val += Q1[a,b] * v[a] * v[b]
216
217
## Sort the solution by it's type
218
#if (Q1(v) == m1):
219
if (tmp_val == m1):
220
solntype = local_solution_type_cdef(Q1, p, v, zvec, nzvec)
221
if (solntype != 0):
222
count_vector[solntype] += 1
223
224
225
## Generate the Bad-type and Total counts
226
count_vector[3] = count_vector[4] + count_vector[5]
227
count_vector[0] = count_vector[1] + count_vector[2] + count_vector[3]
228
229
## Return the solution counts
230
return count_vector
231
232
233
234
235
def CountAllLocalTypesNaive(Q, p, k, m, zvec, nzvec):
236
"""
237
This is an internal routine, which is called by
238
:meth:`sage.quadratic_forms.quadratic_form.QuadraticForm.count_congruence_solutions_by_type
239
QuadraticForm.count_congruence_solutions_by_type`. See the documentation of
240
that method for more details.
241
242
INPUT:
243
244
- `Q` -- quadratic form over `\ZZ`
245
- `p` -- prime number > 0
246
- `k` -- an integer > 0
247
- `m` -- an integer (depending only on mod `p^k`)
248
- ``zvec``, ``nzvec`` -- a list of integers in ``range(Q.dim())``, or ``None``
249
250
OUTPUT:
251
a list of six integers `\ge 0` representing the solution types: ``[All,
252
Good, Zero, Bad, BadI, BadII]``
253
254
255
EXAMPLES::
256
257
sage: from sage.quadratic_forms.count_local_2 import CountAllLocalTypesNaive
258
sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
259
sage: CountAllLocalTypesNaive(Q, 3, 1, 1, None, None)
260
[6, 6, 0, 0, 0, 0]
261
sage: CountAllLocalTypesNaive(Q, 3, 1, 2, None, None)
262
[6, 6, 0, 0, 0, 0]
263
sage: CountAllLocalTypesNaive(Q, 3, 1, 0, None, None)
264
[15, 12, 1, 2, 0, 2]
265
266
"""
267
return CountAllLocalTypesNaive_cdef(Q, p, k, m, zvec, nzvec)
268
269
270
271
272
273
274
275
cdef local_solution_type_cdef(Q, p, w, zvec, nzvec):
276
"""
277
Internal routine to check if a given solution vector w (of Q(w) =
278
m mod p^k) is of a certain local type and satisfies certain
279
congruence conditions mod p.
280
281
NOTE: No internal checking is done to test if p is a prime >=2, or
282
that Q has the same size as w.
283
284
"""
285
cdef long i
286
cdef long n
287
288
n = Q.dim()
289
290
291
## Check if the solution satisfies the zvec "zero" congruence conditions
292
## (either zvec is empty or its components index the zero vector mod p)
293
if (zvec == None) or (len(zvec) == 0):
294
zero_flag = True
295
else:
296
zero_flag = False
297
i = 0
298
while ( (i < len(zvec)) and ((w[zvec[i]] % p) == 0) ): ## Increment so long as our entry is zero (mod p)
299
i += 1
300
if (i == len(zvec)): ## If we make it through all entries then the solution is zero (mod p)
301
zero_flag = True
302
303
304
## DIAGNOSTIC
305
#print "IsLocalSolutionType: Finished the Zero congruence condition test \n"
306
307
if (zero_flag == False):
308
return <long> 0
309
310
## DIAGNOSTIC
311
#print "IsLocalSolutionType: Passed the Zero congruence condition test \n"
312
313
314
## Check if the solution satisfies the nzvec "nonzero" congruence conditions
315
## (nzvec is non-empty and its components index a non-zero vector mod p)
316
if (nzvec == None):
317
nonzero_flag = True
318
elif (len(nzvec) == 0):
319
nonzero_flag = False ## Trivially no solutions in this case!
320
else:
321
nonzero_flag = False
322
i = 0
323
while ((nonzero_flag == False) and (i < len(nzvec))):
324
if ((w[nzvec[i]] % p) != 0):
325
nonzero_flag = True ## The non-zero condition is satisfied when we find one non-zero entry
326
i += 1
327
328
if (nonzero_flag == False):
329
return <long> 0
330
331
332
## Check if the solution has the appropriate (local) type:
333
## -------------------------------------------------------
334
335
## 1: Check Good-type
336
for i from 0 <= i < n:
337
if (((w[i] % p) != 0) and ((Q[i,i] % p) != 0)):
338
return <long> 1
339
if (p == 2):
340
for i from 0 <= i < (n - 1):
341
if (((Q[i,i+1] % p) != 0) and (((w[i] % p) != 0) or ((w[i+1] % p) != 0))):
342
return <long> 1
343
344
345
## 2: Check Zero-type
346
Zero_flag = True
347
for i from 0 <= i < n:
348
if ((w[i] % p) != 0):
349
Zero_flag = False
350
if (Zero_flag == True):
351
return <long> 2
352
353
354
## Check if wS1 is zero or not
355
wS1_nonzero_flag = False
356
for i from 0 <= i < n:
357
358
## Compute the valuation of each index, allowing for off-diagonal terms
359
if (Q[i,i] == 0):
360
if (i == 0):
361
val = valuation(Q[i,i+1], p) ## Look at the term to the right
362
elif (i == n - 1):
363
val = valuation(Q[i-1,i], p) ## Look at the term above
364
else:
365
val = valuation(Q[i,i+1] + Q[i-1,i], p) ## Finds the valuation of the off-diagonal term since only one isn't zero
366
else:
367
val = valuation(Q[i,i], p)
368
369
## Test each index
370
if ((val == 1) and ((w[i] % p) != 0)):
371
wS1_nonzero_flag = True
372
373
374
## 4: Check Bad-type I
375
if (wS1_nonzero_flag == True):
376
#print " Bad I Soln : " + str(w)
377
return <long> 4
378
379
380
## 5: Check Bad-type II
381
if (wS1_nonzero_flag == False):
382
#print " Bad II Soln : " + str(w)
383
return <long> 5
384
385
386
## Error if we get here! =o
387
print " Solution vector is " + str(w)
388
print " and Q is \n" + str(Q) + "\n"
389
raise RuntimeError, "Error in IsLocalSolutionType: Should not execute this line... =( \n"
390
391
392