Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
#################################################################################
2
#
3
# (c) Copyright 2011 William Stein
4
#
5
# This file is part of PSAGE.
6
#
7
# PSAGE is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 2 of the License, or
10
# (at your option) any later version.
11
#
12
# PSAGE is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#
20
#################################################################################
21
22
r"""
23
24
25
"""
26
27
import math, os, sys
28
29
from sage.all import (
30
arrow,
31
bar_chart,
32
cached_function, cached_method,
33
CDF,
34
ceil,
35
divisors,
36
e,
37
Ei,
38
ellipsis_range as erange,
39
exp,
40
factor,
41
finance,
42
floor,
43
gcd,
44
Graphics,
45
graphics_array,
46
is_prime, is_prime_power, is_pseudoprime,
47
latex,
48
Li,
49
line,
50
log, sin, cos,
51
maxima,
52
moebius,
53
parallel,
54
pi,
55
plot,
56
point,
57
points,
58
polygon,
59
prime_divisors,
60
prime_range,
61
prime_pi,
62
prime_powers,
63
primes,
64
prod,
65
randint,
66
random,
67
save,
68
set_random_seed,
69
spline,
70
sqrt,
71
SR,
72
text,
73
var,
74
walltime,
75
zeta,
76
zeta_zeros,
77
ZZ
78
)
79
80
81
##############################################################
82
# Drawing figures (one or all)
83
##############################################################
84
85
def draw(fig=None, dir='illustrations/',ext='pdf', in_parallel=True):
86
if not os.path.exists(dir):
87
os.makedirs(dir)
88
89
if isinstance(fig, str):
90
print "Drawing %s... "%fig,
91
sys.stdout.flush()
92
t = walltime()
93
G = eval('fig_%s(dir,ext)'%fig)
94
print " (time = %s seconds)"%walltime(t)
95
return G
96
97
if fig is None:
98
figs = ['_'.join(x.split('_')[1:]) for x in globals() if x.startswith('fig_')]
99
elif isinstance(fig, list):
100
figs = fig
101
if in_parallel:
102
@parallel
103
def f(x):
104
return draw(x, dir=dir, ext=ext, in_parallel=False)
105
for x in f(figs):
106
print x
107
else:
108
for fig in figs:
109
draw(fig)
110
return
111
112
##############################################################
113
# Factorization trees
114
##############################################################
115
def fig_factor_tree(dir, ext):
116
g = FactorTree(6).plot(labels={'fontsize':60},sort=True)
117
g.save(dir + '/factor_tree_6.%s'%ext, axes=False, axes_pad=0.1)
118
119
g = FactorTree(12).plot(labels={'fontsize':50},sort=True)
120
g.save(dir + '/factor_tree_12.%s'%ext, axes=False, axes_pad=0.1)
121
122
set_random_seed(3)
123
g = FactorTree(12).plot(labels={'fontsize':50},sort=False)
124
g.save(dir + '/factor_tree_12b.%s'%ext, axes=False,axes_pad=0.1)
125
126
set_random_seed(0)
127
for w in ['a', 'b']:
128
g = FactorTree(300).plot(labels={'fontsize':40},sort=False)
129
g.save(dir + '/factor_tree_300_%s.%s'%(w,ext), axes=False, axes_pad=0.1)
130
131
set_random_seed(0)
132
g = FactorTree(6469693230).plot(labels={'fontsize':14},sort=False)
133
g.save(dir + '/factor_tree_big.%s'%ext, axes=False, axes_pad=0.1)
134
135
136
class FactorTree:
137
"""
138
A factorization tree.
139
140
EXAMPLES::
141
142
sage: FactorTree(100)
143
Factorization trees of 100
144
sage: R.<x> = QQ[]
145
sage: FactorTree(x^3-1)
146
Factorization trees of x^3 - 1
147
"""
148
def __init__(self, n):
149
"""
150
INPUT:
151
152
- `n` -- number of element of polynomial ring
153
"""
154
self.__n = n
155
156
def value(self):
157
"""
158
Return the n such that self is FactorTree(n).
159
160
EXAMPLES::
161
162
sage: FactorTree(100).value()
163
100
164
"""
165
return self.__n
166
167
def __repr__(self):
168
"""
169
EXAMPLES::
170
171
sage: FactorTree(100).__repr__()
172
'Factorization trees of 100'
173
"""
174
return "Factorization trees of %s"%self.__n
175
176
def plot(self, lines=None, labels=None, sort=False):
177
"""
178
INPUT:
179
180
- ``lines`` -- optional dictionary of options passed
181
to the line command
182
183
- ``labels`` -- optional dictionary of options passed to
184
the text command for the divisor labels
185
186
- ``sort`` -- bool (default: False); if True, the primes
187
divisors are found in order from smallest to largest;
188
otherwise, the factor tree is draw at random
189
190
EXAMPLES::
191
192
sage: FactorTree(2009).plot(labels={'fontsize':30},sort=True)
193
194
We can make factor trees of polynomials in addition to integers::
195
196
sage: R.<x> = QQ[]
197
sage: F = FactorTree((x^2-1)*(x^3+2)*(x-5)); F
198
Factorization trees of x^6 - 5*x^5 - x^4 + 7*x^3 - 10*x^2 - 2*x + 10
199
sage: F.plot(labels={'fontsize':15},sort=True)
200
"""
201
if lines is None:
202
lines = {'rgbcolor':(.5,.5,1)}
203
if labels is None:
204
labels = {'fontsize':16}
205
n = self.__n
206
rows = []
207
v = [(n,None,0)]
208
self._ftree(rows, v, sort=sort)
209
return self._plot_ftree(rows, lines, labels)
210
211
def _ftree(self, rows, v, sort):
212
"""
213
A function that is used recurssively internally by the plot function.
214
215
INPUT:
216
217
- ``rows`` -- list of lists of integers
218
219
- ``v`` -- list of triples of integers
220
221
- ``sort`` -- bool (default: False); if True, the primes
222
divisors are found in order from smallest to largest;
223
otherwise, the factor tree is draw at random
224
225
EXAMPLES::
226
227
sage: F = FactorTree(100)
228
sage: rows = []; v = [(100,None,0)]; F._ftree(rows, v, True)
229
sage: rows
230
[[(100, None, 0)],
231
[(2, 100, 0), (50, 100, 0)],
232
[(None, None, None), (2, 50, 1), (25, 50, 1)],
233
[(None, None, None), (None, None, None), (5, 25, 2), (5, 25, 2)]]
234
sage: v
235
[(100, None, 0)]
236
"""
237
if len(v) > 0:
238
# add a row to g at the ith level.
239
rows.append(v)
240
w = []
241
for i in range(len(v)):
242
k, _,_ = v[i]
243
if k is None or ZZ(k).is_irreducible():
244
w.append((None,None,None))
245
else:
246
div = divisors(k)
247
if sort:
248
d = div[1]
249
else:
250
z = divisors(k)[1:-1]
251
d = z[randint(0,len(z)-1)]
252
w.append((d,k,i))
253
e = k//d
254
if e == 1:
255
w.append((None,None))
256
else:
257
w.append((e,k,i))
258
if len(w) > len(v):
259
self._ftree(rows, w, sort=sort)
260
261
def _plot_ftree(self, rows, lines, labels):
262
"""
263
Used internally by the plot method.
264
265
INPUT:
266
267
- ``rows`` -- list of lists of triples
268
269
- ``lines`` -- dictionary of options to pass to lines commands
270
271
- ``labels`` -- dictionary of options to pass to text command to label factors
272
273
EXAMPLES::
274
275
sage: F = FactorTree(100)
276
sage: rows = []; v = [(100,None,0)]; F._ftree(rows, v, True)
277
sage: F._plot_ftree(rows, {}, {})
278
"""
279
g = Graphics()
280
for i in range(len(rows)):
281
cur = rows[i]
282
for j in range(len(cur)):
283
e, f, k = cur[j]
284
if not e is None:
285
if ZZ(e).is_irreducible():
286
c = (1,0,0)
287
else:
288
c = (0,0,.4)
289
g += text("$%s$"%latex(e), (j*2-len(cur),-i), rgbcolor=c, **labels)
290
if not k is None and not f is None:
291
g += line([(j*2-len(cur),-i), (k*2-len(rows[i-1]),-i+1)], axes=False, **lines)
292
return g
293
294
295
##############################################################
296
# Bag of primes
297
##############################################################
298
299
def bag_of_primes(steps):
300
"""
301
This works up to 9. It took a day using specialized factoring
302
(via GMP-ECM) to get step 10.
303
304
EXAMPLES::
305
306
sage: bag_of_primes(5)
307
[2, 3, 7, 43, 13, 139, 3263443]
308
"""
309
bag = [2]
310
for i in range(steps):
311
for p in prime_divisors(prod(bag)+1):
312
bag.append(p)
313
print bag
314
315
316
##############################################################
317
# Questions about numbers
318
##############################################################
319
def fig_questions(dir, ext):
320
g = questions(100,17,20)
321
g.save(dir + '/questions.%s'%ext, axes=False)
322
323
def questions(n=100,k=17,fs=20):
324
set_random_seed(k)
325
g = text("?",(5,5),rgbcolor='grey', fontsize=200)
326
g += sum(text("$%s$"%p,(random()*10,random()*10),rgbcolor=(p/(2*n),p/(2*n),p/(2*n)),fontsize=fs)
327
for p in primes(n))
328
return g
329
330
331
##############################################################
332
# Sieve of Eratosthenes
333
##############################################################
334
335
def fig_erat(dir,ext):
336
# sieving out 2,3,5,7
337
for p in [2,3,5,7]:
338
sieve_step(p,100).save(dir+'/sieve100-%s.%s'%(p,ext))
339
340
sieve_step(13,200).save(dir+'/sieve200.%s'%ext)
341
342
343
def number_grid(c, box_args=None, font_args=None, offset=0):
344
"""
345
INPUT:
346
c -- list of length n^2, where n is an integer.
347
The entries of c are RGB colors.
348
box_args -- additional arguments passed to box.
349
font_args -- all additional arguments are passed
350
to the text function, e.g., fontsize.
351
offset -- use to fine tune text placement in the squares
352
353
OUTPUT:
354
Graphics -- a plot of a grid that illustrates
355
those n^2 numbers colored according to c.
356
"""
357
if box_args is None: box_args = {}
358
if font_args is None: font_args = {}
359
360
try:
361
n = sqrt(ZZ(len(c)))
362
except ArithmeticError:
363
raise ArithmeticError, "c must have square length"
364
G = Graphics()
365
k = 0
366
for j in reversed(range(n)):
367
for i in range(n):
368
col = c[int(k)]
369
R = line([(i,j),(i+1,j),(i+1,j+1),(i,j+1),(i,j)],
370
thickness=.2, **box_args)
371
d = dict(box_args)
372
if 'rgbcolor' in d.keys():
373
del d['rgbcolor']
374
P = polygon([(i,j),(i+1,j),(i+1,j+1),(i,j+1),(i,j)],
375
rgbcolor=col, **d)
376
G += P + R
377
if col != (1,1,1):
378
G += text(str(k+1), (i+.5+offset,j+.5), **font_args)
379
k += 1
380
G.axes(False)
381
G.xmin(0); G.xmax(n); G.ymin(0); G.ymax(n)
382
return G
383
384
def sieve_step(p, n, gone=(1,1,1), prime=(1,0,0), \
385
multiple=(.6,.6,.6), remaining=(.9,.9,.9),
386
fontsize=11,offset=0):
387
"""
388
Return graphics that illustrates sieving out multiples of p.
389
Numbers that are a nontrivial multiple of primes < p are shown in
390
the gone color. Numbers that are a multiple of p are shown in a
391
different color. The number p is shown in yet a third color.
392
393
INPUT:
394
p -- a prime (or 1, in which case all points are colored "remaining")
395
n -- a SQUARE integer
396
gone -- rgb color for integers that have been sieved out
397
prime -- rgb color for p
398
multiple -- rgb color for multiples of p
399
remaining -- rgb color for integers that have not been sieved out yet
400
and are not multiples of p.
401
"""
402
if p == 1:
403
c = [remaining]*n
404
else:
405
exclude = prod(prime_range(p)) # exclude multiples of primes < p
406
c = []
407
for k in range(1,n+1):
408
if k <= p and is_prime(k):
409
c.append(prime)
410
elif k == 1 or (gcd(k,exclude) != 1 and not is_prime(k)):
411
c.append(gone)
412
elif k%p == 0:
413
c.append(multiple)
414
else:
415
c.append(remaining)
416
# end for
417
# end if
418
return number_grid(c,{'rgbcolor':(0.2,0.2,0.2)},{'fontsize':fontsize, 'rgbcolor':(0,0,0)},offset=offset)
419
420
421
##############################################################
422
# Similar rates of growth
423
##############################################################
424
425
def fig_simrates(dir,ext):
426
# similar rates
427
G = similar_rates()
428
G.save(dir + "/similar_rates.%s"%ext,figsize=[8,3])
429
430
def similar_rates():
431
"""
432
Draw figure fig:simrates illustrating similar rates.
433
434
EXAMPLES::
435
436
sage: similar_rates()
437
"""
438
X = var('X')
439
A = 2*X**2 + 3*X - 5
440
B = 3*X**2 - 2*X + 1
441
G = plot(A/B, (1,100))
442
G += text("$A(X)/B(X)$", (70,.58), rgbcolor='black', fontsize=14)
443
H = plot(A, (X,1,100), rgbcolor='red') + plot(B, (X,1,100))
444
H += text("$A(X)$", (85,8000), rgbcolor='black',fontsize=14)
445
H += text("$B(X)$", (60,18000), rgbcolor='black',fontsize=14)
446
a = graphics_array([[H,G]])
447
return a
448
449
def same_rates():
450
"""
451
Draw figure fig:samerates illustrating same rates.
452
453
EXAMPLES::
454
455
sage: similar_rates()
456
"""
457
X = var('X')
458
A = X**2 + 3*X - 5
459
B = X**2 - 2*X + 1
460
G = plot(A/B, (1,100))
461
G += text("$A(X)/B(X)$", (70,.58), rgbcolor='black', fontsize=14)
462
H = plot(A, (X,1,100), rgbcolor='red') + plot(B, (X,1,100))
463
H += text("$A(X)$", (70,8500), rgbcolor='black',fontsize=14)
464
H += text("$B(X)$", (90,5000), rgbcolor='black',fontsize=14)
465
a = graphics_array([[H,G]])
466
return a
467
468
def fig_same_rates(dir,ext):
469
# similar rates
470
G = same_rates()
471
G.save(dir + "/same_rates.%s"%ext,figsize=[8,3])
472
473
474
##############################################################
475
# Proportion of Primes to to X
476
##############################################################
477
478
def fig_log(dir, ext):
479
g = plot(log, 1/3.0, 100, thickness=2)
480
g.save(dir + '/log.%s'%ext, figsize=[8,3], gridlines=True)
481
482
def fig_proportion_primes(dir,ext):
483
for bound in [100,1000,10000]:
484
g = proportion_of_primes(bound)
485
g.save(dir + '/proportion_primes_%s.%s'%(bound,ext))
486
487
def plot_step_function(v, vertical_lines=True, **args):
488
r"""
489
Return the line that gives the plot of the step function f defined
490
by the list v of pairs (a,b). Here if (a,b) is in v, then f(a) = b.
491
492
INPUT:
493
494
- `v` -- list of pairs (a,b)
495
496
EXAMPLES::
497
498
sage: plot_step_function([(i,sin(i)) for i in range(5,20)])
499
"""
500
# make sorted copy of v (don't change in place, since that would be rude).
501
v = list(sorted(v))
502
if len(v) <= 1:
503
return line([]) # empty line
504
if vertical_lines:
505
w = []
506
for i in range(len(v)):
507
w.append(v[i])
508
if i+1 < len(v):
509
w.append((v[i+1][0],v[i][1]))
510
return line(w, **args)
511
else:
512
return sum(line([v[i],(v[i+1][0],v[i][1])], **args) for i in range(len(v)-1))
513
514
515
def proportion_of_primes(bound, **args):
516
"""
517
Return a graph of the step function that assigns to X the
518
proportion of numbers between 1 and X of primes.
519
520
INPUTS:
521
522
- `bound` -- positive integer
523
524
- additional arguments are passed to the line function.
525
526
EXAMPLES::
527
528
sage: proportion_of_primes(100)
529
"""
530
v = []
531
k = 0.0
532
for n in range(1,bound+1):
533
if is_prime(n):
534
k += 1
535
v.append((n,k/n))
536
return plot_step_function(v, **args)
537
538
539
##############################################################
540
# Prime Gaps
541
##############################################################
542
543
@cached_function
544
def prime_gaps(maxp):
545
"""
546
Return the sequence of prime gaps obtained using primes up to maxp.
547
548
EXAMPLES::
549
550
sage: prime_gaps(100)
551
[1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8]
552
"""
553
P = prime_range(maxp+1)
554
return [P[i+1] - P[i] for i in range(len(P)-1)]
555
556
@cached_function
557
def prime_gap_distribution(maxp):
558
"""
559
Return list v such that v[i] is how many times i is a prime gap
560
among the primes up to maxp.
561
562
EXAMPLES::
563
564
sage: prime_gap_distribution(100)
565
[0, 1, 8, 0, 7, 0, 7, 0, 1]
566
sage: prime_gap_distribution(1000)
567
[0, 1, 35, 0, 40, 0, 44, 0, 15, 0, 16, 0, 7, 0, 7, 0, 0, 0, 1, 0, 1]
568
"""
569
h = prime_gaps(maxp)
570
v = [0]*(max(h)+1)
571
for gap in h: v[gap] += 1
572
return v
573
574
def fig_primegapdist(dir,ext):
575
v = prime_gap_distribution(10**7)[:50]
576
bar_chart(v).save(dir+"/primegapdist.%s"%ext, figsize=[9,3])
577
578
579
def prime_gap_plots(maxp, gap_sizes):
580
"""
581
Return a list of graphs of the functions Gap_k(X) for 0<=X<=maxp,
582
for each k in gap_sizes. The graphs are lists of pairs (X,Y) of
583
integers.
584
585
INPUT:
586
- maxp -- positive integer
587
- gap_sizes -- list of integers
588
"""
589
P = prime_range(maxp+1)
590
v = [[(0,0)] for i in gap_sizes]
591
k = dict([(g,i) for i, g in enumerate(gap_sizes)])
592
for i in range(len(P)-1):
593
g = P[i+1] - P[i]
594
if g in k:
595
w = v[k[g]]
596
w.append( (P[i+1],w[-1][1]) )
597
w.append( (P[i+1],w[-1][1]+1) )
598
return v
599
600
601
def fig_primegap_race(dir, ext):
602
"""
603
Draw plots showing the race for gaps of size 2, 4, 6, and 8.
604
"""
605
X = 7000
606
gap_sizes = [2,4,6,8]
607
#X = 100000
608
#gap_sizes = [i for i in range(2,50) if i%2==0]
609
v = prime_gap_plots(X, gap_sizes)
610
611
P = sum(line(x) for x in v)
612
P += sum( text( "Gap %s"%gap_sizes[i], (v[i][-1][0]*1.04, v[i][-1][1]), color='black', fontsize=8)
613
for i in range(len(v)))
614
615
P.save(dir + '/primegap_race.%s'%ext, figsize=[9,3], gridlines=True)
616
return P
617
618
619
620
##############################################################
621
# Multiplicatively even and odd table
622
##############################################################
623
def mult_even_odd_count(bound):
624
"""
625
Return list v of length bound such that v[n] is a pair (a,b) where
626
a is the number of multiplicatively even positive numbers <= n and b is the
627
number of multiplicatively odd positive numbers <= n.
628
629
INPUT:
630
631
- ``bound`` -- a positive integer
632
633
EXAMPLES::
634
635
We make the table in the paper::
636
637
sage: mult_even_odd_count(17)
638
[(0, 0), (1, 0), (1, 1), (1, 2), (2, 2), (2, 3), (3, 3), (3, 4), (3, 5), (4, 5), (5, 5), (5, 6), (5, 7), (5, 8), (6, 8), (7, 8), (8, 8)]
639
"""
640
par = mult_parities(bound)
641
a = 0; b = 0
642
v = [(a,b)]
643
for n in range(1,bound):
644
if par[n] == 0: # mult even
645
a += 1
646
else:
647
b += 1
648
v.append((a,b))
649
return v
650
651
def fig_multpar(dir,ext):
652
for n in [10**k for k in reversed([1,2,3,4,5,6])]:
653
file = dir + '/liouville-%s.%s'%(n,ext)
654
if n >= 1000:
655
time_series = True
656
else:
657
time_series = False
658
g = race_mult_parity(n, time_series=time_series)
659
g.save(file, frame=True)
660
661
def race_mult_parity(bound, time_series=False, **kwds):
662
"""
663
Return a plot that shows race between multiplicatively even and
664
odd numbers. More precisely it shows the function f(X) that
665
equals number of even numbers >= 2 and <=X minus the number of odd
666
numbers >= 2 and <= X.
667
668
EXAMPLES::
669
670
sage: race_mult_parity(10^5,time_series=True)
671
sage: race_mult_parity(10^5)
672
"""
673
par = mult_parities(bound)[2:]
674
if not time_series:
675
v = [(2,0)]
676
for n in range(bound-2):
677
if par[n] == 0:
678
b = v[-1][1]+1
679
else:
680
b = v[-1][1]-1
681
v.append((v[-1][0], b))
682
v.append((v[-1][0]+1, b))
683
return line(v, **kwds)
684
else:
685
v = [0,0,0]
686
for n in range(bound-2):
687
if par[n] == 0:
688
v.append(v[-1]+1)
689
else:
690
v.append(v[-1]-1)
691
return finance.TimeSeries(v).plot()
692
693
694
def mult_parities_python(bound, verbose=False):
695
"""
696
Return list v of length bound such that v[n] is 0 if n is
697
multiplicative even, and v[n] is 1 if n is multiplicatively odd.
698
Also v[0] is None.
699
700
This goes up to bound=`10^7` in about 30 seconds.
701
"""
702
v = [None]*bound
703
v[0] = None
704
v[1] = int(0)
705
P = [int(p) for p in prime_range(bound)]
706
for p in P:
707
v[p] = int(1)
708
last = P
709
last_parity = int(1)
710
loops = floor(log(bound,2))+1
711
bound = int(bound)
712
for k in range(loops):
713
cur = []
714
cur_parity = (last_parity+int(1))%int(2)
715
if verbose:
716
print "loop %s (of %s); last = %s"%(k,loops, len(last))
717
for n in last:
718
for p in P:
719
m = n * p
720
if m >= bound:
721
break
722
if v[m] is None:
723
v[m] = cur_parity
724
cur.append(m)
725
last_parity = cur_parity
726
last = cur
727
return v
728
729
##############################################################
730
# LogX over X in "Probabilistic first guess" chapter
731
##############################################################
732
def fig_logXoverX(dir, ext):
733
file = dir + '/logXoverX.%s'%ext
734
x = var('x')
735
xmax = 250
736
G = plot(x/(log(x)-1), 4, xmax, color='blue')
737
G += prime_pi.plot(4, xmax, color='red')
738
G.save(file, figsize=[7,3])
739
740
##############################################################
741
# The devil is in the details.
742
##############################################################
743
744
def committee_pi(X, error_prob=0.1):
745
num_primes = 0
746
for N in range(1, X+1):
747
N_is_prime = is_pseudoprime(N)
748
if random() < error_prob:
749
# make a mistake
750
if not N_is_prime:
751
# incorrectly count it
752
num_primes += 1
753
else:
754
# do not make a mistake
755
if N_is_prime:
756
num_primes += 1
757
return num_primes
758
759
760
761
762
763
##############################################################
764
# Prime pi plots
765
##############################################################
766
767
def fig_prime_pi_aspect1(dir,ext):
768
for n in [25, 100]:
769
p = plot(lambda x: prime_pi(floor(x)), 1,n,
770
plot_points=10000,rgbcolor='red',
771
fillcolor=(.9,.9,.9),fill=True)
772
file = dir + '/prime_pi_%s_aspect1.%s'%(n,ext)
773
p.save(file, aspect_ratio=1, gridlines=True)
774
775
def fig_prime_pi(dir,ext):
776
for n in [1000, 10000, 100000]:
777
p = plot(lambda x: prime_pi(floor(x)), 1,n,
778
plot_points=10000,rgbcolor='red',
779
fillcolor=(.9,.9,.9),fill=True)
780
file = dir + '/prime_pi_%s.%s'%(n,ext)
781
p.save(file)
782
783
def fig_prime_pi_nofill(dir,ext):
784
for n in [25,38,100,1000,10000,100000]:
785
g = plot_prime_pi(n, rgbcolor='red', thickness=2)
786
g.save(dir + '/PN_%s.%s'%(n,ext))
787
788
def plot_prime_pi(n = 25, **args):
789
v = [(0,0)]
790
k = 0
791
for p in prime_range(n+1):
792
k += 1
793
v.append((p,k))
794
v.append((n,k))
795
return plot_step_function(v, **args)
796
797
##############################################################
798
# Sieving
799
##############################################################
800
801
def fig_sieves(dir,ext):
802
plot_three_sieves(100, shade=False).save(dir + '/sieve_2_100.%s'%ext, figsize=[9,3])
803
plot_all_sieves(1000, shade=True).save(dir + '/sieve1000.%s'%ext,figsize=[9,3])
804
805
m=100
806
for z in [3,7]:
807
save(plot_multiple_sieves(m,k=[z]) ,dir+'/sieves%s_100.%s'%(z,ext), xmax=m, figsize=[9,3])
808
809
def plot_sieve(n, x, poly={}, lin={}, label=True, shade=True):
810
"""
811
Return a plot of the number of integer up to x that are coprime to n.
812
These are the integers that are sieved out using the primes <= n.
813
814
In n is 0 draw a graph of all primes.
815
"""
816
v = range(x+1) # integers 0, 1, ..., x
817
if n == 0:
818
v = prime_range(x)
819
else:
820
for p in prime_divisors(n):
821
v = [k for k in v if k%p != 0 or k==p]
822
# eliminate non-prime multiples of p
823
v = set(v)
824
j = 0
825
w = [(0,j)]
826
for i in range(1,x+1):
827
w.append((i,j))
828
if i in v:
829
j += 1
830
w.append((i,j))
831
w.append((i,0))
832
w.append((0,0))
833
if n == 0:
834
t = "Primes"
835
pos = x,.7*j
836
elif n == 1:
837
t = "All Numbers"
838
pos = x, 1.03*j
839
else:
840
P = prime_divisors(n)
841
if len(P) == 1:
842
t = "Sieve by %s"%P[0]
843
else:
844
t = "Sieve by %s"%(', '.join([str(_) for _ in P]))
845
pos = x,1.05*j
846
F = line(w[:-2], **lin)
847
if shade:
848
F += polygon(w, **poly)
849
if label:
850
F += text(t, pos, horizontal_alignment="right", rgbcolor='black')
851
return F
852
853
def plot_three_sieves(m, shade=True):
854
s1 = plot_sieve(1, m, poly={'rgbcolor':(.85,.9,.7)},
855
lin={'rgbcolor':(0,0,0), 'thickness':1}, shade=shade)
856
s2 = plot_sieve(2, m, poly={'rgbcolor':(.75,.8,.6)},
857
lin={'rgbcolor':(0,0,0),'thickness':1}, shade=shade)
858
s3 = plot_sieve(0, m, poly={'rgbcolor':(1,.7,.5)},
859
lin={'rgbcolor':(1,0,0), 'thickness':1}, shade=shade)
860
return s1+s2+s3
861
862
def plot_multiple_sieves(m=100, k = [2,3,5], shade=True):
863
g = Graphics()
864
n = len(k)
865
for i in range(n):
866
c = (1-float(i+1)/n)*0.666
867
if k[i] == 0:
868
z = 0
869
else:
870
z = prod(prime_range(k[i]+1))
871
r = float(i)/n
872
clr = (.85 + 0.15*r,0.9 -0.2*r, 0.9 -0.4*r)
873
if z == 0:
874
clrlin=(1,0,0)
875
else:
876
clrlin=(0,0,0)
877
s = plot_sieve(z, m,
878
poly={'rgbcolor':clr},
879
lin={'rgbcolor':clrlin, 'thickness':1},
880
label=(i==0 or i==n-1), shade=shade)
881
g += s
882
return g
883
884
def plot_all_sieves(x, shade=True):
885
P = [1] + prime_range(int(sqrt(x))+1) + [0]
886
G = plot_multiple_sieves(x, P, shade=shade)
887
return G
888
889
890
##############################################################
891
# Area under plot of 1/log(x)
892
##############################################################
893
894
#auto
895
def area_under_inverse_log(m, **args):
896
r"""
897
This function returns a graphical illustration of `Li(x)` for `x
898
\leq m` viewed as the integral of `1/\log(t)` from 2 to `t`. We
899
also plot primes on the `x`-axis and display the area as text.
900
901
EXAMPLES::
902
903
904
"""
905
f = plot(lambda x: 1/math.log(x), 2, m) # TODO: weird.
906
P = polygon([(2,0)]+list(f[0])+[(m,0)], hue=0.1,alpha=0.4)
907
if False:
908
T = sum([text(str(p),(p,-0.08),vertical_alignment="top",\
909
horizontal_alignment="center", fontsize=6, rgbcolor=(.6,0,0)) \
910
for p in prime_range(m+1)])
911
else:
912
T = Graphics()
913
pr = sum([point((p,0), rgbcolor=(.6,0,0), pointsize=100/log(p)) for p in prime_range(m+1)])
914
L = 'quad_qag(1/log(x), x, 2,%s, 0)'%m
915
fs = 36
916
area = text('Area ~ %f'%(float(maxima(L)[0])),(.5*m,.75),fontsize=fs,rgbcolor='black')
917
primes = text('%s Primes'%len(prime_range(m+1)), (.5*m,-0.3),fontsize=fs,rgbcolor='black')
918
fun = text('1/log(x)',(m/8.0,1.4),fontsize=fs,rgbcolor='black', horizontal_alignment='left')
919
G = pr + f+P+area+T+primes +fun
920
G.xmax(m+1)
921
return G
922
923
def fig_inverse_of_log(dir,ext):
924
for m in [30, 100, 1000]:
925
area_under_inverse_log(m).save(dir+'/area_under_log_graph_%s.%s'%(m,ext), figsize=[7,7])
926
927
928
##############################################################
929
# Comparing Li, pi, and x/log(x)
930
##############################################################
931
def fig_li_pi_loginv(dir,ext):
932
plot_li_pi_loginv(xmax=200).save(dir+'/three_plots.%s'%ext,figsize=[8,3])
933
934
def plot_li_pi_loginv(xmax=200):
935
x = var('x')
936
P = plot(x/log(x), (2, xmax))
937
P+= plot(Li, (2, xmax), rgbcolor='black')
938
P+= plot(prime_pi, 2, xmax, rgbcolor='red')
939
return P
940
941
942
##############################################################
943
# Perspective
944
##############################################################
945
946
def fig_primes_line(dir,ext):
947
xmin=1; xmax=38; pointsize=90
948
g = points([(p,0) for p in prime_range(xmax+1)], pointsize=pointsize, rgbcolor='red')
949
g += line([(xmin,0), (xmax,0)], rgbcolor='black')
950
eps = 1/2.0
951
for n in range(xmin, xmax+1):
952
g += line([(n,eps), (n,-eps)], rgbcolor='black', thickness=0.5)
953
g += text("$%s$"%n, (n,-6), rgbcolor='black')
954
g.save(dir + '/primes_line.%s'%ext, axes=False,figsize=[9,.7], ymin=-10, ymax=2)
955
956
##############################################################
957
# Plots of Psi function
958
##############################################################
959
960
def psi_data(xmax):
961
from math import log, pi
962
v = [(0,0), (1,0), (1,log(2*pi))]
963
y = v[-1][1]
964
for pn in prime_powers(2,xmax+1):
965
y += log(factor(pn)[0][0])
966
v.append( (pn,y) )
967
v.append((xmax,y))
968
return v
969
970
def plot_psi(xmax, **kwds):
971
v = psi_data(xmax)
972
return plot_step_function(v, **kwds)
973
974
def fig_psi(dir,ext):
975
for m in [9,38,100,200]:
976
g = plot_psi(m, thickness=2)
977
g.save(dir+'/psi_%s.%s'%(m,ext), aspect_ratio=1, gridlines=True,
978
fontsize=20)
979
g = plot(lambda x:x,1,1000,rgbcolor='red')+plot_psi(1000,alpha=0.8)
980
g.save(dir+'/psi_diag_%s.%s'%(1000,ext),aspect_ratio=1, fontsize=20, gridlines=True)
981
982
def plot_Psi(xmax, **kwds):
983
v = psi_data(xmax)
984
v = [(log(a),b) for a, b in v if a]
985
return plot_step_function(v, **kwds)
986
987
def fig_Psi(dir, ext):
988
m = 38
989
g = plot_Psi(m, thickness=2)
990
g.save(dir+'/bigPsi_%s.%s'%(m,ext), gridlines=True,
991
fontsize=20, figsize=[6.1,6.1])
992
993
def fig_Psiprime(dir, ext):
994
g = line([(0,0),(0,100)], rgbcolor='black')
995
xmax = 20
996
ymax = 50
997
for n in range(1, xmax+1):
998
if is_prime_power(n):
999
if n == 1:
1000
h = log(2*pi)
1001
else:
1002
h = log(factor(n)[0][0])
1003
c = (float(h)/log(xmax), 0, 1-float(h)/log(xmax))
1004
g += arrow((log(n),-1),(log(n),ymax), width=2, rgbcolor=c)
1005
if n <= 3 or n in [5, 8, 13, 19]:
1006
g += text("log(%s)"%n, (log(n),-5), rgbcolor='black', fontsize=12)
1007
g += line([(log(n),-2), (log(n),0)], rgbcolor='black')
1008
g += line([(-1/2.0,0), (xmax+1,0)], thickness=2)
1009
g.save(dir+'/bigPsi_prime.%s'%ext,
1010
xmin=-1/2.0, xmax=log(xmax), ymax=ymax,
1011
axes=False, gridlines=True, figsize=[8,3])
1012
1013
def fig_Phi(dir=0, ext=0):
1014
g = line([(0,0),(0,100)], rgbcolor='black')
1015
xmax = 20
1016
ymax = 50
1017
for n in range(1, xmax+1):
1018
if is_prime_power(n):
1019
if n == 1:
1020
h = log(2*pi)
1021
else:
1022
h = log(factor(n)[0][0])
1023
h *= exp(-log(n)/2)
1024
c = (float(h)/log(xmax), 0, 1-float(h)/log(xmax))
1025
g += arrow((log(n),-1),(log(n),ymax), width=2, rgbcolor=c)
1026
g += arrow((-log(n),-1),(-log(n),ymax), width=2, rgbcolor=c)
1027
if n in [2, 5, 16]:
1028
g += text("log(%s)"%n, (log(n),-5), rgbcolor='black', fontsize=12)
1029
g += line([(log(n),-2), (log(n),0)], rgbcolor='black')
1030
g += text("log(%s)"%n, (-log(n),-5), rgbcolor='black', fontsize=12)
1031
g += line([(-log(n),-2), (-log(n),0)], rgbcolor='black')
1032
g += line([(-log(xmax)-1,0), (log(xmax)+1,0)], thickness=2)
1033
g.save(dir+'/bigPhi.%s'%ext,
1034
xmin=-log(xmax), xmax=log(xmax), ymax=ymax,
1035
axes=False, gridlines=True, figsize=[8,3])
1036
1037
1038
##############################################################
1039
# Sin, etc. waves
1040
##############################################################
1041
1042
def fig_waves(dir,ext):
1043
g = plot(sin, -2.1*pi, 2.1*pi, thickness=2)
1044
g.save(dir+'/sin.%s'%ext)
1045
1046
x = var('x')
1047
c = 5
1048
# See for why this is right http://www.phy.mtu.edu/~suits/notefreqs.html
1049
g = plot(sin(x), 0, c*pi) + plot(sin(329.0/261*x), 0, c*pi, color='red')
1050
g.save(dir+'/sin-twofreq.%s'%ext)
1051
1052
g = plot(sin(x) + sin(329.0/261*x), 0, c*pi)
1053
g.save(dir+'/sin-twofreq-sum.%s'%ext)
1054
1055
c=5
1056
g = plot(sin(x), -2, c*pi) + plot(sin(x + 1.5), -2, c*pi, color='red')
1057
g += text("Phase", (-3,.5), fontsize=14, rgbcolor='black')
1058
g += arrow((-2.5,.4), (-1.5,0), width=1, rgbcolor='black')
1059
g += arrow((-2,.4), (0,0), width=1, rgbcolor='black')
1060
g.save(dir+'/sin-twofreq-phase.%s'%ext, xmin=-5)
1061
1062
g = plot(sin(x) + sin(329.0/261*x + 0.4), 0, c*pi)
1063
g.save(dir+'/sin-twofreq-phase-sum.%s'%ext)
1064
1065
f = (sin(x) + sin(329.0/261*x + 0.4)).function(x)
1066
g = points([(i,f(i)) for i in erange(0,0.1,Ellipsis,5*pi)])
1067
g.save(dir+'/sin-twofreq-phase-sum-points.%s'%ext)
1068
1069
g = points([(i,f(i)) for i in erange(0,0.1,Ellipsis,5*pi)])
1070
g += plot(f, (0, 5*pi), rgbcolor='black')
1071
g.save(dir+'/sin-twofreq-phase-sum-fill.%s'%ext)
1072
1073
f = (0.7*sin(x) + sin(329.0/261*x + 0.4)).function(x)
1074
g = plot(f, (0, 5*pi))
1075
g.save(dir+'/sound-ce-general_sum.%s'%ext)
1076
1077
B = bar_chart([0,0,0,0.7, 0, 1])
1078
B += text("C", (3.2,-0.05), rgbcolor='black', fontsize=18)
1079
B += text("D", (4.2,-0.05), rgbcolor='black', fontsize=18)
1080
B += text("E", (5.2,-0.05), rgbcolor='black', fontsize=18)
1081
B.save(dir+'/sound-ce-general_sum-blips.%s'%ext, axes=False, xmin=0)
1082
1083
f = (0.7*sin(x) + sin(329.0/261*x + 0.4) + 0.5*sin(300.0/261*x + 0.7) + 0.3*sin(1.5*x + 0.2) + 1.1*sin(4*x+0.1)).function(x)
1084
g = plot(f, (0, 5*pi))
1085
g.save(dir + '/complicated-wave.%s'%ext)
1086
1087
##############################################################
1088
# Pure Tone
1089
##############################################################
1090
def pure_tone(a=2, b=1, theta=1/2.0, tmin=-15, tmax=15):
1091
t = var('t')
1092
return plot(a*cos(b+theta*t), (t,tmin,tmax))
1093
1094
def fig_pure_tone(dir,ext):
1095
g = pure_tone()
1096
g.save(dir + '/pure_tone.%s'%ext, figsize=[8,2])
1097
1098
1099
def mixed_tone3(a=[2,3,5], b=[1,4,-2], theta=[1/2.0,2,-1], tmin=-15, tmax=15):
1100
t = var('t')
1101
f = sum(a[i]*cos(b[i]+theta[i]*t) for i in range(3))
1102
return plot(f, (t,tmin,tmax)), f
1103
1104
def fig_mixed_tone3(dir,ext):
1105
g, f = mixed_tone3()
1106
g.save(dir + '/mixed_tone3.%s'%ext, figsize=[8,2])
1107
1108
1109
1110
1111
##############################################################
1112
# Sawtooth
1113
##############################################################
1114
1115
def fig_sawtooth(dir,ext):
1116
g = plot_sawtooth(3)
1117
g.save(dir+'/sawtooth.%s'%ext, figsize=[10,2])
1118
1119
g = plot_sawtooth_spectrum(18)
1120
g.save(dir+'/sawtooth-spectrum.%s'%ext, figsize=[8,3])
1121
1122
def plot_sawtooth(xmax):
1123
v = []
1124
for x in range(xmax+1):
1125
v += [(x,0), (x+1,1), (x+1,0)]
1126
return line(v)
1127
1128
def plot_sawtooth_spectrum(xmax):
1129
# the spectrum is a spike of height 1/k at k
1130
return sum([line([(k,0),(k,1.0/k)],thickness=3) for k in range(1,xmax+1)])
1131
1132
##############################################################
1133
# Fourier Transforms: second visit
1134
##############################################################
1135
def fig_even_function(dir, ext):
1136
x = var('x')
1137
f = cos(x) + sin(x**2) + sqrt(x)
1138
def g(z):
1139
return f(x=abs(z))
1140
h = plot(g,-4,4)
1141
h.save(dir + '/even_function.%s'%ext, figsize=[9,3])
1142
1143
def fig_even_pi(dir, ext):
1144
g1 = prime_pi.plot(0,50, rgbcolor='red')
1145
g2 = prime_pi.plot(0,50, rgbcolor='red')
1146
g2[0].xdata = [-a for a in g2[0].xdata]
1147
g = g1 + g2
1148
g.save(dir + '/even_pi.%s'%ext, figsize=[10,3],xmin=-49,xmax=49)
1149
1150
def fig_oo_integral(dir, ext):
1151
t = var('t')
1152
f = (1/(t**2+1)*cos(t)).function(t)
1153
a = f.find_root(1,2.5)
1154
b = f.find_root(4,6)
1155
c = f.find_root(7,8)
1156
g = plot(f,(t,-13,13), fill='axis', fillcolor='yellow', fillalpha=1, thickness=2)
1157
g += plot(f,(t,-a,a), fill='axis', fillcolor='grey', fillalpha=1, thickness=2)
1158
g += plot(f,(t,-c,-b), fill='axis', fillcolor='grey', fillalpha=1, thickness=2)
1159
g += plot(f,(t,b,c), fill='axis', fillcolor='grey', fillalpha=1, thickness=2)
1160
g += text(r"$\int_{-\infty}^{\,\infty} f(x) dx$", (-7,0.5), rgbcolor='black', fontsize=30)
1161
#g.show(figsize=[9,3], xmin=-10,xmax=10)
1162
g.save(dir+'/oo_integral.%s'%ext, figsize=[9,3], xmin=-10,xmax=10)
1163
1164
def fig_fourier_machine(dir, ext):
1165
g = text("$f(t)$", (-1/2.0, 1/2.0), fontsize=20, rgbcolor='black')
1166
g += text(r"$\hat{f}(\theta)$", (3/2.0, 1/2.0), fontsize=20, rgbcolor='black')
1167
g += line([(0,0),(0,1),(1,1),(1,0),(0,0)],rgbcolor='black',thickness=3)
1168
g += arrow((-1/2.0 + 1/9.0, 1/2.0), (-1/16.0, 1/2.0), rgbcolor='black')
1169
g += arrow((1 + 1/16.0, 1/2.0), (1 + 1/2.0 - 1/9.0, 1/2.0), rgbcolor='black')
1170
t = var('t')
1171
g += plot((1/2.0)*t*cos(14*t) + 1/2.0, (t,0,1), fill='axis', thickness=0.8)
1172
g.save(dir+'/fourier_machine.%s'%ext, axes=False, axes_pad=0.1)
1173
1174
1175
##############################################################
1176
# Distribution section
1177
##############################################################
1178
1179
def fig_simple_staircase(dir,ext):
1180
v = [(-1,0), (0,1), (1,3), (2,3)]
1181
g = plot_step_function(v, thickness=3, vertical_lines=True)
1182
g.save(dir+'/simple_staircase.%s'%ext)
1183
1184
1185
##############################################################
1186
# Plots of Fourier transform Phihat_even.
1187
##############################################################
1188
1189
def fig_mini_phihat_even(dir,ext):
1190
G = plot_symbolic_phihat(5, 1, 100, 10000, zeros=False)
1191
G.save(dir + "/mini_phihat_even.%s"%ext, figsize=[9,3], ymin=0)
1192
1193
def fig_phihat_even(dir,ext):
1194
for bound in [5, 20, 50, 500]:
1195
G = plot_symbolic_phihat(bound, 2, 100,
1196
plot_points=10**5)
1197
G.save(dir+'/phihat_even-%s.%s'%(bound,ext), figsize=[9,3], ymin=0)
1198
1199
def fig_phihat_even_all(dir, ext):
1200
p = [plot_symbolic_phihat(n, 2,100) for n in [5,20,50,500]]
1201
[a.ymin(0) for a in p]
1202
g = graphics_array([[a] for a in p],4,1)
1203
g.save(dir+'/phihat_even_all.%s'%ext)
1204
1205
def symbolic_phihat(bound):
1206
t = var('t')
1207
f = SR(0)
1208
for pn in prime_powers(bound+1):
1209
if pn == 1: continue
1210
p, e = factor(pn)[0]
1211
f += - log(p)/sqrt(pn) * cos(t*log(pn))
1212
return f
1213
1214
def plot_symbolic_phihat(bound, xmin, xmax, plot_points=1000, zeros=True):
1215
f = symbolic_phihat(bound)
1216
P = plot(f, (t,xmin, xmax), plot_points=plot_points)
1217
if not zeros:
1218
return P
1219
ym = P.ymax()
1220
Z = []
1221
for y in zeta_zeros():
1222
if y > xmax: break
1223
Z.append(y)
1224
zeros = sum([arrow((x,ym),(x,0),rgbcolor='red',width=0.5,arrowsize=2)
1225
for i, x in enumerate(Z)])
1226
return P + zeros
1227
1228
##############################################################
1229
# Calculus pictures
1230
##############################################################
1231
1232
def fig_aplusone(dir,ext):
1233
a = var('a')
1234
g = plot(a+1, -5,8, thickness=3)
1235
g.save(dir + '/graph_aplusone.%s'%ext, gridlines=True, frame=True)
1236
1237
def fig_calculus(dir,ext):
1238
x = var('x')
1239
t = 8; f = log(x); fprime = f.diff()
1240
fontsize = 14
1241
g = plot(f, (0.5,t))
1242
g += plot(x*fprime(x=4)+(f(x=4)-4*fprime(x=4)), (.5,t), rgbcolor='black')
1243
g += point((4,f(x=4)), pointsize=20, rgbcolor='black')
1244
g += plot(fprime, (0.5,t), rgbcolor='red')
1245
g += text("What is the slope of the tangent line?", (3.5,2.2),
1246
fontsize=fontsize, rgbcolor='black')
1247
g += text("Here it is!",(5,.9), fontsize=fontsize, rgbcolor='black')
1248
g += arrow((4.7,.76), (4, fprime(x=4)), rgbcolor='black')
1249
g += point((4,fprime(x=4)),rgbcolor='black', pointsize=20)
1250
g += text("How to compute the slope? This is Calculus.", (4.3, -0.5),
1251
fontsize=fontsize, rgbcolor='black')
1252
g.save(dir + '/graph_slope_deriv.%s'%ext, gridlines=True, frame=True)
1253
1254
def fig_jump(dir,ext):
1255
# straight jump
1256
v = line( [(0,1), (3,1), (3,2), (6,2)], thickness=2)
1257
v.ymin(0)
1258
v.save(dir + '/jump.%s'%ext)
1259
1260
# smooth approximation to a jump
1261
e = .7
1262
v = line( [(0,1), (3-e,1)], thickness=2) + line([(3+e,2), (6,2)], thickness=2)
1263
v.ymin(0)
1264
S = spline( [(3-e,1), (3-e + e/20.0, 1), (3,1.5), (3+e-e/20.0, 2), (3+e,2)] )
1265
v += plot(S, (3-e, 3+e), thickness=2)
1266
v.save(dir + '/jump-smooth.%s'%ext, ymin=0)
1267
1268
# derivatives of smooth jumps
1269
for e in ['7', '2', '05', '01']:
1270
g = smoothderiv(float('.'+e))
1271
g.save(dir + '/jump-smooth-deriv-%s.%s'%(e,ext))
1272
1273
1274
def smoothderiv(e):
1275
def deriv(f, delta):
1276
# return very approximate numerical derivative of callable f, using
1277
# a given choice of delta
1278
def g(x):
1279
return (f(x + delta) - f(x))/delta
1280
return g
1281
1282
v = line( [(0,1), (3-e,1)], thickness=2) + line([(3+e,2), (6,2)], thickness=2)
1283
v.ymin(0)
1284
S = spline( [(3-e,1), (3-e+e/20.0, 1), (3,1.5), (3+e-e/20.0, 2), (3+e,2)] )
1285
v += plot(S, (3-e, 3+e), thickness=2)
1286
D = (line( [(0,0), (3-e,0)], rgbcolor='red', thickness=2) +
1287
line([(3+e,0), (6,0)], rgbcolor='red', thickness=2))
1288
D += plot( deriv(S, e/30.0), (3-e, 3+e), rgbcolor='red', thickness=2)
1289
v += D
1290
return v
1291
1292
1293
def fig_dirac(dir,ext):
1294
g = line([(0,0),(0,100)], rgbcolor='black')
1295
g += arrow((0,-1),(0,50), width=3)
1296
g += line([(-1.2,0), (1.25,0)], thickness=3)
1297
g.save(dir+'/dirac_delta.%s'%ext,
1298
frame=False, xmin=-1, xmax=1, ymax=50, axes=False, gridlines=True)
1299
1300
def fig_two_delta(dir,ext):
1301
g = line([(0,0),(0,100)], rgbcolor='black')
1302
g += arrow((-1/2.0,-1),(-1/2.0,50),width=3)
1303
g += arrow((1/2.0,-1),(1/2.0,50),width=3)
1304
g += line([(-1.2,0), (1.25,0)], thickness=3)
1305
g += text("$-x$", (-1/2.0 - 1/20.0,-4), rgbcolor='black', fontsize=35, horizontal_alignment='center')
1306
g += text("$x$", (1/2.0,-4), rgbcolor='black', fontsize=35, horizontal_alignment='center')
1307
g.save(dir+'/two_delta.%s'%ext,
1308
frame=False, xmin=-1, xmax=1, ymax=50, axes=False, gridlines=True)
1309
1310
##############################################################
1311
# Cosine sums
1312
##############################################################
1313
1314
def fig_phi(dir,ext):
1315
g = phi_approx_plot(2,30,1000)
1316
g.save(dir+'/phi_cos_sum_2_30_1000.%s'%ext, ymin=-5,ymax=160)
1317
1318
g = phi_interval_plot(26, 34)
1319
g.save(dir+'/phi_cos_sum_26_34_1000.%s'%ext, axes=False)
1320
1321
g = phi_interval_plot(1010,1026,15000,drop=60)
1322
g.save(dir+'/phi_cos_sum_1010_1026_15000.%s'%ext, axes=False, ymin=-50)
1323
1324
def phi_interval_plot(xmin, xmax, zeros=1000,fontsize=12,drop=20):
1325
g = phi_approx_plot(xmin,xmax,zeros=zeros,fontsize=fontsize,drop=drop)
1326
g += line([(xmin,0),(xmax,0)],rgbcolor='black')
1327
return g
1328
1329
1330
def phi_approx(m, positive_only=False, symbolic=False):
1331
if symbolic:
1332
assert not positive_only
1333
s = var('s')
1334
return -sum(cos(log(s)*t) for t in zeta_zeros()[:m])
1335
1336
from math import cos, log
1337
v = [float(z) for z in zeta_zeros()[:m]]
1338
def f(s):
1339
s = log(float(s))
1340
return -sum(cos(s*t) for t in v)
1341
if positive_only:
1342
z = float(0)
1343
def g(s):
1344
return max(f(s),z)
1345
return g
1346
else:
1347
return f
1348
1349
def phi_approx_plot(xmin, xmax, zeros, pnts=2000, dots=True, positive_only=False,
1350
fontsize=7, drop=10, **args):
1351
phi = phi_approx(zeros, positive_only)
1352
g = plot(phi, xmin, xmax, alpha=0.7,
1353
plot_points=pnts, adaptive_recursion=0, **args)
1354
g.xmin(xmin); g.xmax(xmax)
1355
if dots: g += primepower_dots(xmin,xmax, fontsize=fontsize,drop=drop)
1356
return g
1357
1358
def primepower_dots(xmin, xmax, fontsize=7, drop=10):
1359
"""
1360
Return plot with dots at the prime powers in the given range.
1361
"""
1362
g = Graphics()
1363
for n in range(max(xmin,2),ceil(xmax)+1):
1364
F = factor(n)
1365
if len(F) == 1:
1366
g += point((n,0), pointsize=50*log(F[0][0]), rgbcolor=(1,0,0))
1367
if fontsize>0:
1368
g += text(str(n),(n,-drop),fontsize=fontsize, rgbcolor='black')
1369
g.xmin(xmin)
1370
g.xmax(xmax)
1371
return g
1372
1373
##############################################################
1374
# psi waves
1375
##############################################################
1376
1377
def fig_psi_waves(dir, ext):
1378
theta, t = var('theta, t')
1379
f = (theta*sin(t*theta) + 1/2.0 * cos(t*theta)) / (theta**2 + 1/4.0)
1380
g = plot(f(theta=zeta_zeros()[0]),(t,0,pi))
1381
g.save(dir + '/psi_just_waves1.%s'%ext)
1382
1383
g += plot(f(theta=zeta_zeros()[1]),(t,0,pi), rgbcolor='red')
1384
g.save(dir + '/psi_2_waves.%s'%ext)
1385
1386
f = (e**(t/2)*theta*sin(t*theta) + 1/2.0 * e**(t/2) * cos(t*theta))/(theta**2 + 1/4.0)
1387
g = plot(f(theta=zeta_zeros()[0]),(t,0,pi))
1388
g.save(dir + '/psi_with_first_zero.%s'%ext)
1389
1390
g += plot(f(theta=zeta_zeros()[1]),(t,0,pi), rgbcolor='red')
1391
g.save(dir + '/psi_with_exp_2.%s'%ext)
1392
1393
1394
##############################################################
1395
# Riemann's R_k
1396
##############################################################
1397
1398
def fig_moebius(dir,ext):
1399
g = plot(moebius,0, 50)
1400
g.save(dir+'/moebius.%s'%ext,figsize=[10,2], axes_pad=.1)
1401
1402
1403
def riemann_R(terms):
1404
c = [0] + [float(moebius(n))/n for n in range(1, terms+1)]
1405
def f(x):
1406
x = float(x)
1407
s = float(0)
1408
for n in range(1,terms+1):
1409
y = x**(1.0/n)
1410
if y < 2:
1411
break
1412
s += c[n] * Li(y)
1413
return s
1414
return f
1415
1416
def plot_pi_riemann_gauss(xmin, xmax, terms):
1417
R = riemann_R(terms)
1418
g = plot(R, xmin, xmax)
1419
g += plot(prime_pi, xmin, xmax, rgbcolor='red')
1420
g += plot(Li, xmin, xmax, rgbcolor='purple')
1421
#x = var('x'); g += plot(x/(log(x)-1), xmin, xmax, rgbcolor='green')
1422
return g
1423
1424
def fig_pi_riemann_gauss(dir,ext):
1425
for m in [100,1000]:
1426
g = plot_pi_riemann_gauss(2,m, 100)
1427
g.save(dir+'/pi_riemann_gauss_%s.%s'%(m,ext))
1428
g = plot_pi_riemann_gauss(10000,11000, 100)
1429
g.save(dir +'/pi_riemann_gauss_10000-11000.%s'%ext, axes=False, frame=True)
1430
1431
1432
class RiemannPiApproximation:
1433
r"""
1434
Riemann's explicit formula for `\pi(X)`.
1435
1436
EXAMPLES::
1437
1438
We compute Riemann's analytic approximatin to `\pi(25)` using `R_{10}(x)`:
1439
1440
sage: R = RiemannPiApproximation(10, 100); R
1441
Riemann explicit formula for pi(x) for x <= 100 using R_k for k <= 10
1442
sage: R.Rk(100, 10)
1443
25.3364299527
1444
sage: prime_pi(100)
1445
25
1446
1447
"""
1448
def __init__(self, kmax, xmax, prec=50):
1449
"""
1450
INPUT:
1451
1452
- ``kmax`` -- (integer) large k allowed
1453
1454
- ``xmax`` -- (float) largest input x value allowed
1455
1456
- ``prec`` -- (default: 50) determines precision of certain series approximations
1457
1458
"""
1459
from math import log
1460
self.xmax = xmax
1461
self.kmax = kmax
1462
self.prec = prec
1463
self.N = int(log(xmax)/log(2))
1464
self.rho_k = [0] + [CDF(0.5, zeta_zeros()[k-1]) for k in range(1,kmax+1)]
1465
self.rho = [[0]+[rho_k / float(n) for n in range(1, self.N+1)] for rho_k in self.rho_k]
1466
self.mu = [float(x) for x in moebius.range(0,self.N+2)]
1467
self.msum = sum([moebius(n) for n in xrange(1,self.N+1)])
1468
self._init_coeffs()
1469
1470
def __repr__(self):
1471
return "Riemann explicit formula for pi(x) for x <= %s using R_k for k <= %s"%(self.xmax, self.kmax)
1472
1473
def _init_coeffs(self):
1474
self.coeffs = [1]
1475
n_factorial = 1.0
1476
for n in xrange(1, self.prec):
1477
n_factorial *= n
1478
zeta_value = float(abs(zeta(n+1)))
1479
self.coeffs.append(float(1.0/(n_factorial*n*zeta_value)))
1480
1481
def _check(self, x, k):
1482
if x > self.xmax:
1483
raise ValueError, "x (=%s) must be at most %s"%(x, self.xmax)
1484
if k > self.kmax:
1485
raise ValueError, "k (=%s) must be at most %s"%(k, self.kmax)
1486
1487
@cached_method
1488
def R(self, x):
1489
from math import log
1490
y = log(x)
1491
z = y
1492
a = float(1)
1493
for n in xrange(1,self.prec):
1494
a += self.coeffs[n]*z
1495
z *= y
1496
return a
1497
1498
@cached_method
1499
def Rk(self, x, k):
1500
return self.R(x) + self.Sk(x, k)
1501
1502
@cached_method
1503
def Sk(self, x, k):
1504
"""
1505
Compute approximate correction term, so Rk(x,k) = R(x) + Sk(x,k)
1506
"""
1507
self._check(x, k)
1508
1509
from math import atan, pi, log
1510
log_x = log(x) # base e
1511
# This is from equation 32 on page 978 of Riesel-Gohl.
1512
term1 = self.msum / (2*log_x) + \
1513
(1/pi) * atan(pi/log_x)
1514
1515
# This is from equation 19 on page 975
1516
term2 = sum(self.Tk(x, v) for v in xrange(1,k+1))
1517
return term1 + term2
1518
1519
@cached_method
1520
def Tk(self, x, k):
1521
"""
1522
Compute sum from 1 to N of
1523
mu(n)/n * ( -2*sqrt(x) * cos(im(rho_k/n)*log(x) \
1524
- arg(rho_k/n)) / ( pi_over_2 * log(x) )
1525
"""
1526
self._check(x, k)
1527
x = float(x)
1528
log_x = log(x)
1529
val = float(0)
1530
rho_k = self.rho_k[k]
1531
rho = self.rho[k]
1532
for n in xrange(1, self.N+1):
1533
rho_k_over_n = rho[n]
1534
mu_n = self.mu[n]
1535
if mu_n != 0:
1536
z = Ei( rho_k_over_n * log_x)
1537
val += (mu_n/float(n)) * (2*z).real()
1538
return -val
1539
1540
def plot_Rk(self, k, xmin=2, xmax=None, **kwds):
1541
r"""
1542
Plot `\pi(x)` and `R_k` between ``xmin`` and ``xmax``. If `k`
1543
is a list, also plot every `R_k`, for `k` in the list.
1544
1545
The **kwds are passed onto the line function, which is used
1546
to draw the plot of `R_k`.
1547
"""
1548
if not xmax:
1549
xmax = self.xmax
1550
else:
1551
if xmax > self.xmax:
1552
raise ValueError, "xmax must be at most %s"%self.xmax
1553
xmax = min(self.xmax, xmax)
1554
if kwds.has_key('plot_points'):
1555
plot_points = kwds['plot_points']
1556
del kwds['plot_points']
1557
else:
1558
plot_points = 100
1559
eps = float(xmax-xmin)/plot_points
1560
if not isinstance(k, list):
1561
k = [k]
1562
f = sum(line([(x,self.Rk(x,kk)) for x in erange(xmin,xmin+eps,Ellipsis,xmax)], **kwds)
1563
for kk in k)
1564
g = prime_pi.plot(xmin, xmax, rgbcolor='red')
1565
return g+f
1566
1567
1568
def fig_Rk(dir, ext):
1569
R = RiemannPiApproximation(50, 500, 50)
1570
for k,xmin,xmax in [(1,2,100), (10,2,100), (25,2,100),
1571
(50,2,100), (50,2,500) ]:
1572
print "plotting k=%s"%k
1573
g = R.plot_Rk(k, xmin, xmax, plot_points=300, thickness=0.65)
1574
g.save(dir + '/Rk_%s_%s_%s.%s'%(k,xmin,xmax,ext))
1575
#
1576
g = R.plot_Rk(50, 350, 400, plot_points=200)
1577
g += plot(Li,350,400,rgbcolor='green')
1578
g.save(dir + '/Rk_50_350_400.%s'%ext)
1579
1580
1581
1582
1583
#################################################################
1584
# Import fast cython versions of some functions, if available.
1585
1586
try:
1587
from psage.rh.mazur_stein.book_cython import mult_parities
1588
except ImportError, msg:
1589
print msg
1590
print "Cython versions of some functions not available."
1591
mult_parieties = mult_parities_python
1592
1593
1594