William Stein -- Talk for Mathematics is a long conversation: a celebration of Barry Mazur
1# Options23##############################################################4# Drawing figures (one or all)5##############################################################67def draw(fig=None, dir='illustrations/',ext='pdf'):8if isinstance(fig, str):9print "Drawing %s... "%fig,10sys.stdout.flush()11t = walltime()12eval('fig_%s(dir,ext)'%fig)13print " (time = %s seconds)"%walltime(t)14return1516if fig is None:17figs = ['_'.join(x.split('_')[1:]) for x in globals() if x.startswith('fig_')]18elif isinstance(fig, list):19figs = fig20for fig in figs:21draw(fig)22return2324##############################################################25# Factorization trees26##############################################################27def fig_factor_tree(dir, ext):28g = FactorTree(6).plot(labels={'fontsize':60},sort=True)29g.save(dir + '/factor_tree_6.%s'%ext, axes=False, axes_pad=0.1, typeset='latex')3031g = FactorTree(12).plot(labels={'fontsize':50},sort=True)32g.save(dir + '/factor_tree_12.%s'%ext, axes=False, axes_pad=0.1, typeset='latex')3334set_random_seed(3)35g = FactorTree(12).plot(labels={'fontsize':50},sort=False)36g.save(dir + '/factor_tree_12b.%s'%ext, axes=False,axes_pad=0.1, typeset='latex')3738set_random_seed(0)39for w in ['a', 'b']:40g = FactorTree(300).plot(labels={'fontsize':40},sort=False)41g.save(dir + '/factor_tree_300_%s.%s'%(w,ext), axes=False, axes_pad=0.1, typeset='latex')4243set_random_seed(0)44g = FactorTree(6469693230).plot(labels={'fontsize':14},sort=False)45g.save(dir + '/factor_tree_big.%s'%ext, axes=False, axes_pad=0.1, typeset='latex')464748class FactorTree:49"""50A factorization tree.5152EXAMPLES::5354sage: FactorTree(100)55Factorization trees of 10056sage: R.<x> = QQ[]57sage: FactorTree(x^3-1)58Factorization trees of x^3 - 159"""60def __init__(self, n):61"""62INPUT:6364- `n` -- number of element of polynomial ring65"""66self.__n = n6768def value(self):69"""70Return the n such that self is FactorTree(n).7172EXAMPLES::7374sage: FactorTree(100).value()7510076"""77return self.__n7879def __repr__(self):80"""81EXAMPLES::8283sage: FactorTree(100).__repr__()84'Factorization trees of 100'85"""86return "Factorization trees of %s"%self.__n8788def plot(self, lines=None, labels=None, sort=False):89"""90INPUT:9192- ``lines`` -- optional dictionary of options passed93to the line command9495- ``labels`` -- optional dictionary of options passed to96the text command for the divisor labels9798- ``sort`` -- bool (default: False); if True, the primes99divisors are found in order from smallest to largest;100otherwise, the factor tree is draw at random101102EXAMPLES::103104sage: FactorTree(2009).plot(labels={'fontsize':30},sort=True)105106We can make factor trees of polynomials in addition to integers::107108sage: R.<x> = QQ[]109sage: F = FactorTree((x^2-1)*(x^3+2)*(x-5)); F110Factorization trees of x^6 - 5*x^5 - x^4 + 7*x^3 - 10*x^2 - 2*x + 10111sage: F.plot(labels={'fontsize':15},sort=True)112"""113if lines is None:114lines = {'rgbcolor':(.5,.5,1)}115if labels is None:116labels = {'fontsize':16}117n = self.__n118rows = []119v = [(n,None,0)]120self._ftree(rows, v, sort=sort)121return self._plot_ftree(rows, lines, labels)122123def _ftree(self, rows, v, sort):124"""125A function that is used recurssively internally by the plot function.126127INPUT:128129- ``rows`` -- list of lists of integers130131- ``v`` -- list of triples of integers132133- ``sort`` -- bool (default: False); if True, the primes134divisors are found in order from smallest to largest;135otherwise, the factor tree is draw at random136137EXAMPLES::138139sage: F = FactorTree(100)140sage: rows = []; v = [(100,None,0)]; F._ftree(rows, v, True)141sage: rows142[[(100, None, 0)],143[(2, 100, 0), (50, 100, 0)],144[(None, None, None), (2, 50, 1), (25, 50, 1)],145[(None, None, None), (None, None, None), (5, 25, 2), (5, 25, 2)]]146sage: v147[(100, None, 0)]148"""149if len(v) > 0:150# add a row to g at the ith level.151rows.append(v)152w = []153for i in range(len(v)):154k, _,_ = v[i]155if k is None or k.is_irreducible():156w.append((None,None,None))157else:158div = divisors(k)159if sort:160d = div[1]161else:162z = divisors(k)[1:-1]163d = z[randint(0,len(z)-1)]164w.append((d,k,i))165e = k//d166if e == 1:167w.append((None,None))168else:169w.append((e,k,i))170if len(w) > len(v):171self._ftree(rows, w, sort=sort)172173def _plot_ftree(self, rows, lines, labels):174"""175Used internally by the plot method.176177INPUT:178179- ``rows`` -- list of lists of triples180181- ``lines`` -- dictionary of options to pass to lines commands182183- ``labels`` -- dictionary of options to pass to text command to label factors184185EXAMPLES::186187sage: F = FactorTree(100)188sage: rows = []; v = [(100,None,0)]; F._ftree(rows, v, True)189sage: F._plot_ftree(rows, {}, {})190"""191g = Graphics()192for i in range(len(rows)):193cur = rows[i]194for j in range(len(cur)):195e, f, k = cur[j]196if not e is None:197if e.is_irreducible():198c = (1r,0r,0r)199else:200c = (0r,0r,.4r)201g += text("$%s$"%latex(e), (j*2-len(cur),-i), rgbcolor=c, **labels)202if not k is None and not f is None:203g += line([(j*2-len(cur),-i), (k*2-len(rows[i-1]),-i+1)], axes=False, **lines)204return g205206207##############################################################208# Bag of primes209##############################################################210211def bag_of_primes(steps):212"""213This works up to 9. It took a day using specialized factoring214(via GMP-ECM) to get step 10.215216EXAMPLES::217218sage: bag_of_primes(5)219[2, 3, 7, 43, 13, 139, 3263443]220"""221bag = [2]222for i in range(steps):223for p in prime_divisors(prod(bag)+1):224bag.append(p)225print bag226227228##############################################################229# Questions about numbers230##############################################################231def fig_questions(dir, ext):232g = questions(100,17,20)233g.save(dir + '/questions.%s'%ext, axes=False, typeset='latex')234235def questions(n=100,k=17,fs=20):236set_random_seed(k)237g = text("?",(5,5),rgbcolor='grey', fontsize=200)238g += sum(text("$%s$"%p,(random()*10,random()*10),rgbcolor=(p/(2*n),p/(2*n),p/(2*n)),fontsize=fs)239for p in primes(n))240return g241242243##############################################################244# Sieve of Eratosthenes245##############################################################246247def fig_erat(dir,ext):248# sieving out 2,3,5,7249for p in [2,3,5,7]:250sieve_step(p,100).save(dir+'/sieve100-%s.%s'%(p,ext), typeset='latex')251252sieve_step(13,200).save(dir+'/sieve200.%s'%ext, typeset='latex')253254255def number_grid(c, box_args=None, font_args=None, offset=0):256"""257INPUT:258c -- list of length n^2, where n is an integer.259The entries of c are RGB colors.260box_args -- additional arguments passed to box.261font_args -- all additional arguments are passed262to the text function, e.g., fontsize.263offset -- use to fine tune text placement in the squares264265OUTPUT:266Graphics -- a plot of a grid that illustrates267those n^2 numbers colored according to c.268"""269if box_args is None: box_args = {}270if font_args is None: font_args = {}271272try:273n = sqrt(ZZ(len(c)))274except ArithmeticError:275raise ArithmeticError, "c must have square length"276G = Graphics()277k = 0278for j in reversed(range(n)):279for i in range(n):280col = c[int(k)]281R = line([(i,j),(i+1,j),(i+1,j+1),(i,j+1),(i,j)],282thickness=.2, **box_args)283d = dict(box_args)284if 'rgbcolor' in d.keys():285del d['rgbcolor']286P = polygon([(i,j),(i+1,j),(i+1,j+1),(i,j+1),(i,j)],287rgbcolor=col, **d)288G += P + R289if col != (1,1,1):290G += text(str(k+1), (i+.5+offset,j+.5), **font_args)291k += 1292G.axes(False)293G.xmin(0); G.xmax(n); G.ymin(0); G.ymax(n)294G.set_aspect_ratio('automatic')295return G296297def sieve_step(p, n, gone=(1,1,1), prime=(1,0,0), \298multiple=(.6,.6,.6), remaining=(.9,.9,.9),299fontsize=11,offset=0):300"""301Return graphics that illustrates sieving out multiples of p.302Numbers that are a nontrivial multiple of primes < p are shown in303the gone color. Numbers that are a multiple of p are shown in a304different color. The number p is shown in yet a third color.305306INPUT:307p -- a prime (or 1, in which case all points are colored "remaining")308n -- a SQUARE integer309gone -- rgb color for integers that have been sieved out310prime -- rgb color for p311multiple -- rgb color for multiples of p312remaining -- rgb color for integers that have not been sieved out yet313and are not multiples of p.314"""315if p == 1:316c = [remaining]*n317else:318exclude = prod(prime_range(p)) # exclude multiples of primes < p319c = []320for k in range(1,n+1):321if k <= p and is_prime(k):322c.append(prime)323elif k == 1 or (gcd(k,exclude) != 1 and not is_prime(k)):324c.append(gone)325elif k%p == 0:326c.append(multiple)327else:328c.append(remaining)329# end for330# end if331return number_grid(c,{'rgbcolor':(0.2,0.2,0.2)},{'fontsize':fontsize, 'rgbcolor':(0,0,0)},offset=offset)332333334##############################################################335# Similar rates of growth336##############################################################337338def fig_simrates(dir,ext):339# similar rates340G = similar_rates()341G.save(dir + "/similar_rates.%s"%ext,figsize=[8,3], typeset='latex')342343def similar_rates():344"""345Draw figure fig:simrates illustrating similar rates.346347EXAMPLES::348349sage: similar_rates()350"""351var('X')352A = 2*X^2 + 3*X - 5353B = 3*X^2 - 2*X + 1354G = plot(A/B, (1,100))355G += text("$A(X)/B(X)$", (70,.58), rgbcolor='black', fontsize=14)356H = plot(A, (X,1,100), rgbcolor='red') + plot(B, (X,1,100))357H += text("$A(X)$", (85,8000), rgbcolor='black',fontsize=14)358H += text("$B(X)$", (60,18000), rgbcolor='black',fontsize=14)359a = graphics_array([[H,G]])360return a361362363364365##############################################################366# Proportion of Primes to to X367##############################################################368369def fig_log(dir, ext):370g = plot(log, 1/3, 100, thickness=2)371g.save(dir + '/log.%s'%ext, figsize=[8,3], gridlines=True, fontsize=18, typeset='latex')372373def fig_proportion_primes(dir,ext):374for bound in [100,1000,10000]:375g = proportion_of_primes(bound)376g.save(dir + '/proportion_primes_%s.%s'%(bound,ext), typeset='latex')377378def plot_step_function(v, vertical_lines=True, **args):379r"""380Return the line that gives the plot of the step function f defined381by the list v of pairs (a,b). Here if (a,b) is in v, then f(a) = b.382383INPUT:384385- `v` -- list of pairs (a,b)386387EXAMPLES::388389sage: plot_step_function([(i,sin(i)) for i in range(5,20)])390"""391# make sorted copy of v (don't change in place, since that would be rude).392v = list(sorted(v))393if len(v) <= 1:394return line([]) # empty line395if vertical_lines:396w = []397for i in range(len(v)):398w.append(v[i])399if i+1 < len(v):400w.append((v[i+1][0],v[i][1]))401return line(w, **args)402else:403return sum(line([v[i],(v[i+1][0],v[i][1])], **args) for i in range(len(v)-1))404405406def proportion_of_primes(bound, **args):407"""408Return a graph of the step function that assigns to X the409proportion of numbers between 1 and X of primes.410411INPUTS:412413- `bound` -- positive integer414415- additional arguments are passed to the line function.416417EXAMPLES::418419sage: proportion_of_primes(100)420"""421v = []422k = 0423for n in range(1,bound+1):424if is_prime(n):425k += 1426v.append((n,k/n))427return plot_step_function(v, **args)428429430##############################################################431# Prime Gaps432##############################################################433434@cached_function435def prime_gaps(maxp):436"""437Return the sequence of prime gaps obtained using primes up to maxp.438439EXAMPLES::440441sage: prime_gaps(100)442[1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8]443"""444P = prime_range(maxp+1)445return [P[i+1] - P[i] for i in range(len(P)-1)]446447@cached_function448def prime_gap_distribution(maxp):449"""450Return list v such that v[i] is how many times i is a prime gap451among the primes up to maxp.452453EXAMPLES::454455sage: prime_gap_distribution(100)456[0, 1, 8, 0, 7, 0, 7, 0, 1]457sage: prime_gap_distribution(1000)458[0, 1, 35, 0, 40, 0, 44, 0, 15, 0, 16, 0, 7, 0, 7, 0, 0, 0, 1, 0, 1]459"""460h = prime_gaps(maxp)461v = [0]*(max(h)+1)462for gap in h: v[gap] += 1463return v464465def fig_primegapdist(dir,ext):466v = prime_gap_distribution(10^7)[:50]467b = bar_chart(v)468b.save(dir+"/primegapdist.%s"%ext, fontsize=18, figsize=[9,3], ticks=[[2,4,6,8]+[10,20,..,40]+[48],[20000,60000,90000]], tick_formatter=['latex','latex'], typeset='latex')469470"""471# The table in the book...472473def f(B):474v = prime_gap_distribution(10^B)475z = [v[i] if i<len(v) else 0 for i in [2,4,6,8,100,252]]476print "$10^{%s}$ & "%B + " & ".join([str(a) for a in z]) + r"\\\hline"477478for B in [1..8]:479f(B)480"""481482483def prime_gap_plots(maxp, gap_sizes):484"""485Return a list of graphs of the functions Gap_k(X) for 0<=X<=maxp,486for each k in gap_sizes. The graphs are lists of pairs (X,Y) of487integers.488489INPUT:490- maxp -- positive integer491- gap_sizes -- list of integers492"""493P = prime_range(maxp+1)494v = [[(0,0)] for i in gap_sizes]495k = dict([(g,i) for i, g in enumerate(gap_sizes)])496for i in range(len(P)-1):497g = P[i+1] - P[i]498if g in k:499w = v[k[g]]500w.append( (P[i+1],w[-1][1]) )501w.append( (P[i+1],w[-1][1]+1) )502return v503504505def fig_primegap_race(dir, ext):506"""507Draw plots showing the race for gaps of size 2, 4, 6, and 8.508"""509X = 7000510gap_sizes = [2,4,6,8]511#X = 100000512#gap_sizes = [i for i in range(2,50) if i%2==0]513v = prime_gap_plots(X, gap_sizes)514515P = sum(line(x) for x in v)516P += sum( text( "Gap %s"%gap_sizes[i], (v[i][-1][0]*1.04, v[i][-1][1]), color='black', fontsize=8)517for i in range(len(v)))518519P.save(dir + '/primegap_race.%s'%ext, figsize=[9,3], gridlines=True, typeset='latex')520return P521522523524##############################################################525# Multiplicatively even and odd table526##############################################################527def mult_even_odd_count(bound):528"""529Return list v of length bound such that v[n] is a pair (a,b) where530a is the number of multiplicatively even positive numbers <= n and b is the531number of multiplicatively odd positive numbers <= n.532533INPUT:534535- ``bound`` -- a positive integer536537EXAMPLES::538539We make the table in the paper::540541sage: mult_even_odd_count(17)542[(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)]543"""544par = mult_parities_python(bound)545a = 0; b = 0546v = [(a,b)]547for n in range(1,bound):548if par[n] == 0: # mult even549a += 1550else:551b += 1552v.append((a,b))553return v554555def fig_multpar(dir,ext):556for n in [10^k for k in reversed([1,2,3,4,5,6])] + [17]:557file = dir + '/liouville-%s.%s'%(n,ext)558if n >= 1000:559time_series = True560else:561time_series = False562g = race_mult_parity(n, time_series=time_series)563g.save(file, frame=True, fontsize=20, typeset='latex')564565def race_mult_parity(bound, time_series=False, **kwds):566"""567Return a plot that shows race between multiplicatively even and568odd numbers. More precisely it shows the function f(X) that569equals number of even numbers >= 2 and <=X minus the number of odd570numbers >= 2 and <= X.571572EXAMPLES::573574sage: race_mult_parity(10^5,time_series=True)575sage: race_mult_parity(10^5)576"""577par = mult_parities_python(bound)[2:]578if not time_series:579v = [(2,0)]580for n in range(bound-2):581if par[n] == 0:582b = v[-1][1]+1583else:584b = v[-1][1]-1585v.append((v[-1][0], b))586v.append((v[-1][0]+1, b))587return line(v, **kwds)588else:589v = [0,0,0]590for n in range(bound-2):591if par[n] == 0:592v.append(v[-1]+1)593else:594v.append(v[-1]-1)595return finance.TimeSeries(v).plot()596597def mult_parities_python(bound, verbose=False):598"""599Return list v of length bound such that v[n] is 0 if n is600multiplicative even, and v[n] is 1 if n is multiplicatively odd.601Also v[0] is None.602603This goes up to bound=`10^7` in about 30 seconds.604"""605v = [None]*bound606v[0] = None607v[1] = int(0)608P = [int(p) for p in prime_range(bound)]609for p in P:610v[p] = int(1)611last = P612last_parity = int(1)613loops = floor(log(bound,2))+1614bound = int(bound)615for k in range(loops):616cur = []617cur_parity = (last_parity+int(1))%int(2)618if verbose:619print "loop %s (of %s); last = %s"%(k,loops, len(last))620for n in last:621for p in P:622m = n * p623if m >= bound:624break625if v[m] is None:626v[m] = cur_parity627cur.append(m)628last_parity = cur_parity629last = cur630return v631632##############################################################633# LogX over X in "Probabilistic first guess" chapter634##############################################################635def fig_logXoverX(dir, ext):636file = dir + '/logXoverX.%s'%ext637x = var('x')638xmax = 250639G = plot(x/(log(x)-1), 4, xmax, color='blue')640G += prime_pi.plot(4, xmax, color='red')641G.save(file, figsize=[7,3], typeset='latex')642643##############################################################644# Prime pi plots645##############################################################646647def fig_prime_pi_aspect1(dir,ext):648for n in [25, 100]:649p = plot(lambda x: prime_pi(floor(x)), 1,n,650plot_points=10000,rgbcolor='red',651fillcolor=(.9,.9,.9),fill=True)652file = dir + '/prime_pi_%s_aspect1.%s'%(n, ext)653p.save(file, aspect_ratio=1, fontsize=16, typeset='latex')654655def fig_prime_pi(dir,ext):656for n in [1000, 10000, 100000]:657p = plot(lambda x: prime_pi(floor(x)), 1,n,658plot_points=10000,rgbcolor='red',659fillcolor=(.9,.9,.9),fill=True)660file = dir + '/prime_pi_%s.%s'%(n, ext)661if n <100000:662p.save(file, fontsize=16, typeset='latex')663else:664p.save(file, fontsize=16, ticks=[[n/2, n], None], tick_formatter=['latex', None], typeset='latex')665666def fig_prime_pi_nofill(dir,ext):667for n in [25,38,100,1000,10000]:668g = plot_prime_pi(n, rgbcolor='red', thickness=2, fontsize=20)669g.save(dir + '/PN_%s.%s'%(n,ext), typeset='latex')670n = 100000671g = plot_prime_pi(n, rgbcolor='red', thickness=1, fontsize=16, ticks=[[20000,60000,100000],[2000,5000,8000]], tick_formatter=['latex','latex'])672g.save(dir + '/PN_%s.%s'%(n,ext), typeset='latex')673674def plot_prime_pi(n = 25, **args):675v = [(0,0)]676k = 0677for p in prime_range(n+1):678k += 1679v.append((p,k))680v.append((n,k))681return plot_step_function(v, **args)682683def fig_pi_Li(dir, ext):684g = plot_prime_pi(n = 250, rgbcolor='red', thickness=1, fontsize=16)685h = plot(Li, 1, 250)686(g+h).save(dir + 'pi_Li.%s'%ext, typeset='latex')687688##############################################################689# Sieving690##############################################################691692def fig_sieves(dir,ext):693plot_three_sieves(100, shade=False).save(dir + '/sieve_2_100.%s'%ext, figsize=[9,3], fontsize=18, typeset='latex')694plot_all_sieves(1000, shade=True).save(dir + '/sieve1000.%s'%ext,figsize=[9,3], fontsize=18, typeset='latex')695696m=100697for z in [3,7]:698save(plot_multiple_sieves(m,k=[z]) ,dir+'/sieves%s_100.%s'%(z,ext), xmax=m, figsize=[9,3], fontsize=18)699700def plot_sieve(n, x, poly={}, lin={}, label=True, shade=True):701"""702Return a plot of the number of integer up to x that are coprime to n.703These are the integers that are sieved out using the primes <= n.704705In n is 0 draw a graph of all primes.706"""707v = range(x+1) # integers 0, 1, ..., x708if n == 0:709v = prime_range(x)710else:711for p in prime_divisors(n):712v = [k for k in v if k%p != 0 or k==p]713# eliminate non-prime multiples of p714v = set(v)715j = 0716w = [(0,j)]717for i in range(1,x+1):718w.append((i,j))719if i in v:720j += 1721w.append((i,j))722w.append((i,0))723w.append((0,0))724if n == 0:725t = "Primes"726pos = x,.7*j727elif n == 1:728t = "All Numbers"729pos = x, 1.03*j730else:731P = prime_divisors(n)732if len(P) == 1:733t = "Sieve by %s"%P[0]734else:735t = "Sieve by %s"%(', '.join([str(_) for _ in P]))736pos = x,1.05*j737F = line(w[:-2], **lin)738if shade:739F += polygon(w, **poly)740if label:741F += text(t, pos, horizontal_alignment="right", rgbcolor='black', fontsize=18)742F.set_aspect_ratio('automatic')743return F744745def plot_three_sieves(m, shade=True):746s1 = plot_sieve(1, m, poly={'rgbcolor':(.85,.9,.7)},747lin={'rgbcolor':(0,0,0), 'thickness':1}, shade=shade)748s2 = plot_sieve(2, m, poly={'rgbcolor':(.75,.8,.6)},749lin={'rgbcolor':(0,0,0),'thickness':1}, shade=shade)750s3 = plot_sieve(0, m, poly={'rgbcolor':(1,.7,.5)},751lin={'rgbcolor':(1,0,0), 'thickness':1}, shade=shade)752return s1+s2+s3753754def plot_multiple_sieves(m=100, k = [2,3,5], shade=True):755g = Graphics()756n = len(k)757for i in range(n):758c = (1-float(i+1)/n)*0.666759if k[i] == 0:760z = 0761else:762z = prod(prime_range(k[i]+1))763r = float(i)/n764clr = (.85 + 0.15*r,0.9 -0.2*r, 0.9 -0.4*r)765if z == 0:766clrlin=(1,0,0)767else:768clrlin=(0,0,0)769s = plot_sieve(z, m,770poly={'rgbcolor':clr},771lin={'rgbcolor':clrlin, 'thickness':1},772label=(i==0 or i==n-1), shade=shade)773g += s774return g775776def plot_all_sieves(x, shade=True):777P = [1] + prime_range(int(sqrt(x))+1) + [0]778G = plot_multiple_sieves(x, P, shade=shade)779return G780781782##############################################################783# Area under plot of 1/log(x)784##############################################################785786#auto787def area_under_inverse_log(m, **args):788r"""789This function returns a graphical illustration of `Li(x)` for `x790\leq m` viewed as the integral of `1/\log(t)` from 2 to `t`. We791also plot primes on the `x`-axis and display the area as text.792793EXAMPLES::794795796"""797f = plot(lambda x: 1/(log(x)),2,m)798P = polygon([(2,0)]+list(f[0])+[(m,0)], hue=0.1,alpha=0.4)799if False:800T = sum([text(str(p),(p,-0.08),vertical_alignment="top",\801horizontal_alignment="center", fontsize=6, rgbcolor=(.6,0,0)) \802for p in prime_range(m+1)])803else:804T = Graphics()805pr = sum([point((p,0), rgbcolor=(.6,0,0), pointsize=100/log(p)) for p in prime_range(m+1)])806L = 'quad_qag(1/log(x), x, 2,%s, 0)'%m807fs = 36808area = text('Area ~ %f'%(float(maxima(L)[0])),(.5*m,.75),fontsize=fs,rgbcolor='black')809primes = text('%s Primes'%len(prime_range(m+1)), (.5*m,-0.3),fontsize=fs,rgbcolor='black')810fun = text('1/log(x)',(m/8,1.4),fontsize=fs,rgbcolor='black', horizontal_alignment='left')811G = pr + f+P+area+T+primes +fun812G.xmax(m+1)813G.set_aspect_ratio('automatic')814return G815816def fig_inverse_of_log(dir,ext):817for m in [30, 100, 1000]:818area_under_inverse_log(m).save(dir+'/area_under_log_graph_%s.%s'%(m,ext), figsize=[7,7], typeset='latex')819820821##############################################################822# Comparing Li, pi, and x/log(x)823##############################################################824def fig_li_pi_loginv(dir,ext):825plot_li_pi_loginv(xmax=200).save(dir+'/three_plots.%s'%ext,figsize=[8,3], typeset='latex')826827def plot_li_pi_loginv(xmax=200):828var('x')829P = plot(x/log(x), (2, xmax))830P+= plot(Li, (2, xmax), rgbcolor='black')831P+= plot(prime_pi, 2, xmax, rgbcolor='red')832return P833834835##############################################################836# Perspective837##############################################################838839def fig_primes_line(dir,ext):840xmin=1; xmax=38; pointsize=90841g = points([(p,0) for p in prime_range(xmax+1)], pointsize=pointsize, rgbcolor='red')842g += line([(xmin,0), (xmax,0)], rgbcolor='black')843eps = 1/2844for n in [xmin..xmax]:845g += line([(n,eps), (n,-eps)], rgbcolor='black', thickness=0.5)846g += text("$%s$"%n, (n,-6), rgbcolor='black')847g.save(dir + '/primes_line.%s'%ext, axes=False,figsize=[9,.7], ymin=-10, ymax=2, typeset='latex')848849##############################################################850# Plots of Psi function851##############################################################852853def psi_data(xmax):854from math import log, pi855v = [(0,0), (1,0), (1,log(2*pi))]856y = v[-1][1]857for pn in prime_powers(2,xmax+1):858y += log(factor(pn)[0][0])859v.append( (pn,y) )860v.append((xmax,y))861return v862863def plot_psi(xmax, **kwds):864v = psi_data(xmax)865return plot_step_function(v, **kwds)866867def fig_psi(dir,ext):868for m in [9,38,100,200]:869g = plot_psi(m, thickness=2)870g.save(dir+'/psi_%s.%s'%(m,ext), aspect_ratio=1, gridlines=True,871fontsize=16, figsize=4, typeset='latex')872g = plot(lambda x:x,1,1000,rgbcolor='red', thickness=.7) + plot_psi(1000,alpha=0.8, thickness=.7)873g.save(dir+'/psi_diag_%s.%s'%(1000,ext),aspect_ratio=1, gridlines=True, fontsize=12, figsize=4, typeset='latex')874875def plot_Psi(xmax, **kwds):876v = psi_data(xmax)877v = [(log(a),b) for a, b in v if a]878return plot_step_function(v, **kwds)879880def fig_Psi(dir, ext):881m = 38882g = plot_Psi(m, thickness=2)883g.save(dir+'/bigPsi_%s.%s'%(m,ext), gridlines=True,884fontsize=20, figsize=[6.1,6.1], typeset='latex')885886def fig_Psiprime(dir, ext):887g = line([(0,0),(0,100)], rgbcolor='black')888xmax = 20889for n in [1..xmax]:890if is_prime_power(n):891if n == 1:892h = log(2*pi)893else:894h = log(factor(n)[0][0])895h = float(h)*50896g += arrow((log(n),-1),(log(n),h), width=2)897g += line([(log(n),-2), (log(n),150)], rgbcolor='black')898if n <= 3 or n in [5, 8, 13, 19]:899g += text("log(%s)"%n, (log(n),-8), rgbcolor='black', fontsize=12)900g += line([(log(n),-2), (log(n),0)], rgbcolor='black')901g += line([(-1/2,0), (xmax+1,0)], thickness=2)902g.save(dir+'/bigPsi_prime.%s'%ext,903xmin=-1/2, xmax=log(xmax),904axes=False, gridlines=True, figsize=[8,3], typeset='latex')905906def fig_Phi(dir=0, ext=0):907g = line([(0,0),(0,100)], rgbcolor='black')908xmax = 20909ymax = 50910for n in [1..xmax]:911if is_prime_power(n):912if n == 1:913h = log(2*pi)914else:915h = log(factor(n)[0][0])916h /= sqrt(n)917h = float(h)*50918g += arrow((log(n),0),(log(n),h), width=1)919g += arrow((-log(n),0),(-log(n),h), width=1)920g += line([(log(n),0),(log(n),100)], color='black', thickness=.3)921g += line([(-log(n),0),(-log(n),100)], color='black', thickness=.3)922if n in [2, 5, 16]:923g += text("log(%s)"%n, (log(n),-5), rgbcolor='black', fontsize=12)924g += line([(log(n),-2), (log(n),0)], rgbcolor='black')925g += text("log(%s)"%n, (-log(n),-5), rgbcolor='black', fontsize=12)926g += line([(-log(n),-2), (-log(n),0)], rgbcolor='black')927g += line([(-log(xmax)-1,0), (log(xmax)+1,0)], thickness=2)928929g.save(dir+'/bigPhi.%s'%ext,930xmin=-log(xmax), xmax=log(xmax), ymax=ymax,931axes=False, figsize=[8,3], typeset='latex')932933934##############################################################935# Sin, etc. waves936##############################################################937938def fig_waves(dir,ext):939g = plot(sin, -2.1*pi, 2.1*pi, thickness=2)940g.save(dir+'/sin.%s'%ext, fontsize=20, typeset='latex')941942x = var('x')943c = 5944# See for why this is right http://www.phy.mtu.edu/~suits/notefreqs.html945g = plot(sin(x), 0, c*pi) + plot(sin(329.0/261*x), 0, c*pi, color='red')946g.save(dir+'/sin-twofreq.%s'%ext, fontsize=20, typeset='latex')947948g = plot(sin(x) + sin(329.0/261*x), 0, c*pi)949g.save(dir+'/sin-twofreq-sum.%s'%ext, fontsize=20, typeset='latex')950951c=5952g = plot(sin(x), -2, c*pi) + plot(sin(x + 1.5), -2, c*pi, color='red')953g += text("Phase", (-2.5,.5), fontsize=14, rgbcolor='black')954g += arrow((-2.5,.4), (-1.5,0), width=1, rgbcolor='black')955g += arrow((-2,.4), (0,0), width=1, rgbcolor='black')956g.save(dir+'/sin-twofreq-phase.%s'%ext, fontsize=20, typeset='latex')957958g = plot(sin(x) + sin(329.0/261*x + 0.4), 0, c*pi)959g.save(dir+'/sin-twofreq-phase-sum.%s'%ext, fontsize=20, typeset='latex')960961f(x) = sin(x) + sin(329.0/261*x + 0.4)962g = points([(i,f(i)) for i in [0,0.1,..,5*pi]])963g.save(dir+'/sin-twofreq-phase-sum-points.%s'%ext, fontsize=20, typeset='latex')964965g = points([(i,f(i)) for i in [0,0.1,..,5*pi]])966g += plot(f, (0, 5*pi), rgbcolor='black')967g.save(dir+'/sin-twofreq-phase-sum-fill.%s'%ext, fontsize=20, typeset='latex')968969f(x) = 0.7*sin(x) + sin(329.0/261*x + 0.4)970g = plot(f, (0, 5*pi))971g.save(dir+'/sound-ce-general_sum.%s'%ext, fontsize=20, typeset='latex')972973B = bar_chart([0,0,0,0.7, 0, 1])974B += text("C", (3.2,-0.05), rgbcolor='black', fontsize=18)975B += text("D", (4.2,-0.05), rgbcolor='black', fontsize=18)976B += text("E", (5.2,-0.05), rgbcolor='black', fontsize=18)977B.save(dir+'/sound-ce-general_sum-blips.%s'%ext, axes=False, xmin=0, fontsize=20, typeset='latex')978979f(x) = 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)980g = plot(f, (0, 5*pi))981g.save(dir + '/complicated-wave.%s'%ext, fontsize=20, typeset='latex')982983##############################################################984# Sawtooth985##############################################################986987def fig_sawtooth(dir,ext):988g = plot_sawtooth(3)989g.save(dir+'/sawtooth.%s'%ext, figsize=[8,3], fontsize=20, typeset='latex')990991g = plot_sawtooth_spectrum(18)992g.save(dir+'/sawtooth-spectrum.%s'%ext, figsize=[8,3], fontsize=20, typeset='latex')993994def plot_sawtooth(xmax):995v = []996for x in [0..xmax]:997v += [(x,0), (x+1,1), (x+1,0)]998return line(v)9991000def plot_sawtooth_spectrum(xmax):1001# the spectrum is a spike of height 1/k at k1002return sum([line([(k,0),(k,1/k)],thickness=3) for k in [1..xmax]])100310041005##############################################################1006# Pure tone1007##############################################################1008def fig_pure_tone(dir,ext):1009t = var('t')1010g = plot(2 * cos(1+t/2), -15, 15, thickness=2)1011g.save(dir+'/pure_tone.%s'%ext, figsize=[8,3], fontsize=20, typeset='latex')1012101310141015##############################################################1016# Mixed tone1017##############################################################1018def fig_mixed_tone(dir,ext):1019t = var('t')1020f = 5 *cos(-t - 2) + 2 * cos(t/2 + 1) + 3 *cos(2 *t + 4)1021g = plot(f, -15, 15, thickness=2)1022g.save(dir+'/mixed_tone3.%s'%ext, figsize=[8,3], fontsize=20, typeset='latex')10231024##############################################################1025# Fourier Transforms: second visit1026##############################################################1027def fig_even_function(dir, ext):1028x = var('x')1029f = cos(x) + sin(x^2) + sqrt(x)1030def g(z):1031return f(x=abs(z))1032h = plot(g,-4,4)1033h.save(dir + '/even_function.%s'%ext, figsize=[9,3], fontsize=18, typeset='latex')10341035def fig_even_pi(dir, ext):1036g1 = prime_pi.plot(0,50, rgbcolor='red')1037g2 = prime_pi.plot(0,50, rgbcolor='red')1038g2[0].xdata = [-a for a in g2[0].xdata]1039g = g1 + g21040g.save(dir + '/even_pi.%s'%ext, figsize=[10,3],xmin=-49,xmax=49, fontsize=18, typeset='latex')10411042def fig_oo_integral(dir, ext):1043t = var('t')1044f(t) = 1/(t^2+1)*cos(t)1045a = f.find_root(1,2.5)1046b = f.find_root(4,6)1047c = f.find_root(7,8)1048g = plot(f,(t,-13,13), fill='axis', fillcolor='yellow', fillalpha=1, thickness=2)1049g += plot(f,(t,-a,a), fill='axis', fillcolor='grey', fillalpha=1, thickness=2)1050g += plot(f,(t,-c,-b), fill='axis', fillcolor='grey', fillalpha=1, thickness=2)1051g += plot(f,(t,b,c), fill='axis', fillcolor='grey', fillalpha=1, thickness=2)1052g += text(r"$\int_{-\infty}^{\,\infty} f(x) dx$", (-7,0.5), rgbcolor='black', fontsize=30)1053#g.show(figsize=[9,3], xmin=-10,xmax=10)1054g.save(dir+'/oo_integral.%s'%ext, figsize=[9,3], xmin=-10,xmax=10, typeset='latex')10551056def fig_fourier_machine(dir, ext):1057g = text("$f(t)$", (-1/2,1/2), fontsize=30, rgbcolor='black')1058g += text(r"$\hat{f}(\theta)$", (3/2,1/2), fontsize=30, rgbcolor='black')1059g += line([(0,0),(0,1),(1,1),(1,0),(0,0)],rgbcolor='black',thickness=3)1060g += arrow((-1/2+1/9,1/2), (-1/16,1/2), rgbcolor='black')1061g += arrow((1+1/16,1/2), (1+1/2-1/9,1/2), rgbcolor='black')1062t=var('t')1063g += plot((1/2)*t*cos(14*t)+1/2,(t,0,1), fill='axis', thickness=0.8)1064g.save(dir+'/fourier_machine.%s'%ext, axes=False, axes_pad=0.1, typeset='latex')106510661067##############################################################1068# Distribution section1069##############################################################10701071def fig_simple_staircase(dir,ext):1072v = [(-1,0), (0,1), (1,3), (2,3)]1073g = plot_step_function(v, thickness=3, vertical_lines=True)1074g.save(dir+'/simple_staircase.%s'%ext, fontsize=20, typeset='latex')107510761077##############################################################1078# Plots of Fourier transform Phihat_even.1079##############################################################10801081def fig_mini_phihat_even(dir,ext):1082G = plot_symbolic_phihat(5, 1, 100, 10000, zeros=False)1083G.save(dir + "/mini_phihat_even.%s"%ext, figsize=[9,3], ymin=0, fontsize=18, typeset='latex')10841085def fig_phihat_even(dir,ext):1086for bound in [5, 20, 50, 500]:1087G = plot_symbolic_phihat(bound, 2, 100,1088plot_points=10^5)1089G.save(dir+'/phihat_even-%s.%s'%(bound,ext), figsize=[9,3], ymin=0, fontsize=18, typeset='latex')10901091def fig_phihat_even_all(dir, ext):1092p = [plot_symbolic_phihat(n, 2,100) for n in [5,20,50,500]]1093[a.ymin(0) for a in p]1094g = graphics_array([[a] for a in p],4,1)1095g.save(dir+'/phihat_even_all.%s'%ext, typeset='latex')10961097def symbolic_phihat(bound):1098t = var('t')1099f = SR(0)1100for pn in prime_powers(bound+1):1101if pn == 1: continue1102p, e = factor(pn)[0]1103f += - log(p)/sqrt(pn) * cos(t*log(pn))1104return f11051106def plot_symbolic_phihat(bound, xmin, xmax, plot_points=1000, zeros=True):1107f = symbolic_phihat(bound)1108P = plot(f, (t,xmin, xmax), plot_points=plot_points)1109if not zeros:1110return P1111ym = P.ymax()1112Z = []1113for y in zeta_zeros():1114if y > xmax: break1115Z.append(y)1116zeros = sum([arrow((x,ym),(x,0),rgbcolor='red',width=0.5,arrowsize=2)1117for i, x in enumerate(Z)])1118return P + zeros11191120##############################################################1121# Calculus pictures1122##############################################################11231124def fig_aplusone(dir,ext):1125a = var('a')1126g = plot(a+1, -5,8, thickness=3)1127g.save(dir + '/graph_aplusone.%s'%ext, gridlines=True, frame=True, fontsize=20, typeset='latex')11281129def fig_calculus(dir,ext):1130x = var('x')1131t = 8; f = log(x); fprime = f.diff()1132fontsize = 161133g = plot(f, (0.5,t), thickness=2)1134g += plot(x*fprime(x=4)+(f(x=4)-4*fprime(x=4)), (-.5,t), rgbcolor='green', thickness=2, zorder=10)1135g += point((4,f(x=4)), pointsize=50, rgbcolor='black', zorder=20)1136g += plot(fprime, (0.5,t), rgbcolor='red', thickness=2, zorder=15)1137g += text("What is the slope of the tangent line?", (3.5,2.2),1138fontsize=fontsize, rgbcolor='black')1139g += text("Here it is!",(5,.8), fontsize=fontsize, rgbcolor='black')1140g += arrow((4.7,.64), (4.05, fprime(x=4.05)+.08), rgbcolor='black')1141g += point((4,fprime(x=4)),rgbcolor='black', pointsize=50, zorder=20)1142g += text("How to compute the slope? This is Calculus.", (4.3, -0.3),1143fontsize=fontsize, rgbcolor='black')1144g.save(dir + '/graph_slope_deriv.%s'%ext, gridlines=True, frame=True, fontsize=16, typeset='latex')11451146def fig_jump(dir,ext):1147# straight jump1148v = line( [(0,1), (3,1), (3,2), (6,2)], thickness=2)1149v.ymin(0)1150v.save(dir + '/jump.%s'%ext, fontsize=20, typeset='latex')11511152# smooth approximation to a jump1153e = .71154v = line( [(0,1), (3-e,1)], thickness=2) + line([(3+e,2), (6,2)], thickness=2)1155v.ymin(0)1156S = spline( [(3-e,1), (3-e+e/20, 1), (3,1.5), (3+e-e/20, 2), (3+e,2)] )1157v += plot(S, (3-e, 3+e), thickness=2)1158v.save(dir + '/jump-smooth.%s'%ext, fontsize=20, typeset='latex')11591160# derivatives of smooth jumps1161for e in ['7', '2', '05', '01']:1162g = smoothderiv(float('.'+e))1163g.save(dir + '/jump-smooth-deriv-%s.%s'%(e,ext), fontsize=20, typeset='latex')116411651166def smoothderiv(e):1167def deriv(f, delta):1168# return very approximate numerical derivative of callable f, using1169# a given choice of delta1170def g(x):1171return (f(x + delta) - f(x))/delta1172return g11731174v = line( [(0,1), (3-e,1)], thickness=2) + line([(3+e,2), (6,2)], thickness=2)1175v.ymin(0)1176S = spline( [(3-e,1), (3-e+e/20, 1), (3,1.5), (3+e-e/20, 2), (3+e,2)] )1177v += plot(S, (3-e, 3+e), thickness=2)1178D = (line( [(0,0), (3-e,0)], rgbcolor='red', thickness=2) +1179line([(3+e,0), (6,0)], rgbcolor='red', thickness=2))1180D += plot( deriv(S, e/30), (3-e, 3+e), rgbcolor='red', thickness=2)1181v += D1182return v118311841185def fig_dirac(dir,ext):1186g = line([(0,0),(0,100)], rgbcolor='black')1187g += arrow((0,-1),(0,50), width=3)1188g += line([(-1.2,0), (1.25,0)], thickness=3)1189g.save(dir+'/dirac_delta.%s'%ext,1190frame=False, xmin=-1, xmax=1, ymax=50, axes=False, gridlines=True, fontsize=20, typeset='latex')11911192def fig_two_delta(dir,ext):1193g = line([(0,0),(0,100)], rgbcolor='black')1194g += arrow((-1/2,-1),(-1/2,50),width=3)1195g += arrow((1/2,-1),(1/2,50),width=3)1196g += line([(-1.2,0), (1.25,0)], thickness=3)1197g += text("$-x$", (-1/2-1/20,-4), rgbcolor='black', fontsize=35, horizontal_alignment='center')1198g += text("$x$", (1/2,-4), rgbcolor='black', fontsize=35, horizontal_alignment='center')1199g.save(dir+'/two_delta.%s'%ext,1200frame=False, xmin=-1, xmax=1, ymax=50, axes=False, gridlines=True, fontsize=20, typeset='latex')12011202##############################################################1203# Cosine sums1204##############################################################12051206def fig_phi(dir,ext):1207fontsize = 181208g = phi_approx_plot(2,30,1000, fontsize=fontsize)1209g.save(dir+'/phi_cos_sum_2_30_1000.%s'%ext, axes=False, ymin=-5,ymax=160, fontsize=fontsize, typeset='latex')12101211g = phi_interval_plot(26, 34, fontsize=fontsize)1212g.save(dir+'/phi_cos_sum_26_34_1000.%s'%ext, axes=False, fontsize=fontsize, typeset='latex')12131214g = phi_interval_plot(1010,1026,15000,drop=60, fontsize=fontsize)1215g.save(dir+'/phi_cos_sum_1010_1026_15000.%s'%ext, axes=False, ymin=-50, fontsize=fontsize, typeset='latex')12161217def phi_interval_plot(xmin, xmax, zeros=1000,fontsize=12,drop=20):1218g = phi_approx_plot(xmin,xmax,zeros=zeros,fontsize=fontsize,drop=drop)1219g += line([(xmin,0),(xmax,0)],rgbcolor='black')1220return g122112221223def phi_approx(m, positive_only=False, symbolic=False):1224if symbolic:1225assert not positive_only1226s = var('s')1227return -sum(cos(log(s)*t) for t in zeta_zeros()[:m])12281229from math import cos, log1230v = [float(z) for z in zeta_zeros()[:m]]1231def f(s):1232s = log(float(s))1233return -sum(cos(s*t) for t in v)1234if positive_only:1235z = float(0)1236def g(s):1237return max(f(s),z)1238return g1239else:1240return f12411242def phi_approx_plot(xmin, xmax, zeros, pnts=2000, dots=True, positive_only=False,1243fontsize=7, drop=10, **args):1244phi = phi_approx(zeros, positive_only)1245g = plot(phi, xmin, xmax, alpha=0.7,1246plot_points=pnts, adaptive_recursion=0, **args)1247g.xmin(xmin); g.xmax(xmax)1248if dots: g += primepower_dots(xmin,xmax, fontsize=fontsize,drop=drop)1249return g12501251def primepower_dots(xmin, xmax, fontsize=7, drop=10):1252"""1253Return plot with dots at the prime powers in the given range.1254"""1255g = Graphics()1256for n in range(max(xmin,2),ceil(xmax)+1):1257F = factor(n)1258if len(F) == 1:1259g += point((n,0), pointsize=50*log(F[0][0]), rgbcolor=(1,0,0))1260if fontsize>0:1261g += text(str(n),(n,-drop),fontsize=fontsize, rgbcolor='black')1262g.xmin(xmin)1263g.xmax(xmax)1264return g12651266##############################################################1267# psi waves1268##############################################################12691270def fig_psi_waves(dir, ext):1271theta, t = var('theta, t')1272f = (theta*sin(t*theta) + 1/2 * cos(t*theta)) / (theta^2 + 1/4)1273g = plot(f(theta=zeta_zeros()[0]),(t,0,pi))1274g.save(dir + '/psi_just_waves1.%s'%ext, typeset='latex')12751276g += plot(f(theta=zeta_zeros()[1]),(t,0,pi), rgbcolor='red')1277g.save(dir + '/psi_2_waves.%s'%ext, typeset='latex')12781279f = (e^(t/2)*theta*sin(t*theta) + 1/2 * e^(t/2) * cos(t*theta))/(theta^2 + 1/4)1280g = plot(f(theta=zeta_zeros()[0]),(t,0,pi))1281g.save(dir + '/psi_with_first_zero.%s'%ext, typeset='latex')12821283g += plot(f(theta=zeta_zeros()[1]),(t,0,pi), rgbcolor='red')1284g.save(dir + '/psi_with_exp_2.%s'%ext, typeset='latex')128512861287##############################################################1288# Riemann's R_k1289##############################################################12901291def fig_moebius(dir,ext):1292g = plot(moebius,0, 50)1293g.save(dir+'/moebius.%s'%ext,figsize=[10,2], axes_pad=.1, fontsize=14, typeset='latex')129412951296def riemann_R(terms):1297c = [0] + [float(moebius(n))/n for n in range(1, terms+1)]1298def f(x):1299x = float(x)1300s = float(0)1301for n in range(1,terms+1):1302y = x^float(1/n)1303if y < 2:1304break1305s += c[n] * Li(y)1306return s1307return f13081309def plot_pi_riemann_gauss(xmin, xmax, terms):1310R = riemann_R(terms)1311g = plot(R, xmin, xmax)1312g += plot(prime_pi, xmin, xmax, rgbcolor='red')1313g += plot(Li, xmin, xmax, rgbcolor='purple')1314#x = var('x'); g += plot(x/(log(x)-1), xmin, xmax, rgbcolor='green')1315return g13161317def fig_pi_riemann_gauss(dir,ext):1318for m in [100,1000]:1319g = plot_pi_riemann_gauss(2,m, 100)1320g.save(dir+'/pi_riemann_gauss_%s.%s'%(m,ext), fontsize=20, typeset='latex')1321g = plot_pi_riemann_gauss(10000,11000, 100)1322g.save(dir +'/pi_riemann_gauss_10000-11000.%s'%ext, axes=False, frame=True, fontsize=20, typeset='latex')132313241325class RiemannPiApproximation:1326r"""1327Riemann's explicit formula for `\pi(X)`.13281329EXAMPLES::13301331We compute Riemann's analytic approximatin to `\pi(25)` using `R_{10}(x)`:13321333sage: R = RiemannPiApproximation(10, 100); R1334Riemann explicit formula for pi(x) for x <= 100 using R_k for k <= 101335sage: R.Rk(100, 10)133625.33642995271337sage: prime_pi(100)13382513391340"""1341def __init__(self, kmax, xmax, prec=50):1342"""1343INPUT:13441345- ``kmax`` -- (integer) large k allowed13461347- ``xmax`` -- (float) largest input x value allowed13481349- ``prec`` -- (default: 50) determines precision of certain series approximations13501351"""1352from math import log1353self.xmax = xmax1354self.kmax = kmax1355self.prec = prec1356self.N = int(log(xmax)/log(2))1357self.rho_k = [0] + [CDF(0.5, zeta_zeros()[k-1]) for k in range(1,kmax+1)]1358self.rho = [[0]+[rho_k / n for n in range(1, self.N+1)] for rho_k in self.rho_k]1359self.mu = [float(x) for x in moebius.range(0,self.N+2)]1360self.msum = sum([moebius(n) for n in xrange(1,self.N+1)])1361self._init_coeffs()13621363def __repr__(self):1364return "Riemann explicit formula for pi(x) for x <= %s using R_k for k <= %s"%(self.xmax, self.kmax)13651366def _init_coeffs(self):1367self.coeffs = [1]1368n_factorial = 1.01369for n in xrange(1, self.prec):1370n_factorial *= n1371zeta_value = float(abs(zeta(n+1)))1372self.coeffs.append(float(1.0/(n_factorial*n*zeta_value)))13731374def _check(self, x, k):1375if x > self.xmax:1376raise ValueError, "x (=%s) must be at most %s"%(x, self.xmax)1377if k > self.kmax:1378raise ValueError, "k (=%s) must be at most %s"%(k, self.kmax)13791380@cached_method1381def R(self, x):1382from math import log1383y = log(x)1384z = y1385a = float(1)1386for n in xrange(1,self.prec):1387a += self.coeffs[n]*z1388z *= y1389return a13901391@cached_method1392def Rk(self, x, k):1393return self.R(x) + self.Sk(x, k)13941395@cached_method1396def Sk(self, x, k):1397"""1398Compute approximate correction term, so Rk(x,k) = R(x) + Sk(x,k)1399"""1400self._check(x, k)14011402from math import atan, pi, log1403log_x = log(x) # base e1404# This is from equation 32 on page 978 of Riesel-Gohl.1405term1 = self.msum / (2*log_x) + \1406(1/pi) * atan(pi/log_x)14071408# This is from equation 19 on page 9751409term2 = sum(self.Tk(x, v) for v in xrange(1,k+1))1410return term1 + term214111412@cached_method1413def Tk(self, x, k):1414"""1415Compute sum from 1 to N of1416mu(n)/n * ( -2*sqrt(x) * cos(im(rho_k/n)*log(x) \1417- arg(rho_k/n)) / ( pi_over_2 * log(x) )1418"""1419self._check(x, k)1420x = float(x)1421log_x = log(x)1422val = float(0)1423rho_k = self.rho_k[k]1424rho = self.rho[k]1425for n in xrange(1, self.N+1):1426rho_k_over_n = rho[n]1427mu_n = self.mu[n]1428if mu_n != 0:1429z = Ei( rho_k_over_n * log_x)1430val += (mu_n/float(n)) * (2*z).real()1431return -val14321433def plot_Rk(self, k, xmin=2, xmax=None, **kwds):1434r"""1435Plot `\pi(x)` and `R_k` between ``xmin`` and ``xmax``. If `k`1436is a list, also plot every `R_k`, for `k` in the list.14371438The **kwds are passed onto the line function, which is used1439to draw the plot of `R_k`.1440"""1441if not xmax:1442xmax = self.xmax1443else:1444if xmax > self.xmax:1445raise ValueError, "xmax must be at most %s"%self.xmax1446xmax = min(self.xmax, xmax)1447if kwds.has_key('plot_points'):1448plot_points = kwds['plot_points']1449del kwds['plot_points']1450else:1451plot_points = 1001452eps = float(xmax-xmin)/plot_points1453if not isinstance(k, list):1454k = [k]1455f = sum(line([(x,self.Rk(x,kk)) for x in [xmin,xmin+eps,..,xmax]], **kwds)1456for kk in k)1457g = prime_pi.plot(xmin, xmax, rgbcolor='red')1458return g+f145914601461def fig_Rk(dir, ext):1462R = RiemannPiApproximation(50, 500, 50)1463for k,xmin,xmax in [(1,2,100), (10,2,100), (25,2,100),1464(50,2,100), (50,2,500) ]:1465print "plotting k=%s"%k1466g = R.plot_Rk(k, xmin, xmax, plot_points=300, thickness=0.65)1467g.save(dir + '/Rk_%s_%s_%s.%s'%(k,xmin,xmax,ext), typeset='latex')1468#1469g = R.plot_Rk(50, 350, 400, plot_points=200)1470g += plot(Li,350,400,rgbcolor='green')1471g.save(dir + '/Rk_50_350_400.%s'%ext, aspect_ratio=1, typeset='latex')147214731474##############################################################1475# Gaussian Primes1476##############################################################14771478def gaussian_primes(B):1479K.<i> = QuadraticField(-1)1480v = K.primes_of_bounded_norm(2*B*B)1481w = []1482for I in v:1483a = I.gens_reduced()[0]1484if abs(a[0])<=B and abs(a[1]) <= B:1485w.extend([a,-a,i*a,-i*a])1486w = [z for z in w if z[0]>0 and z[1]>=0]1487return w14881489def fig_gaussian_primes(dir, ext):1490B = 101491w = gaussian_primes(B)1492points(w, pointsize=90, zorder=10).save("%s/gaussian_primes-%s.%s"%(dir,B,ext), aspect_ratio=1, gridlines=True, ticks=[[-1..B],[-1..B]], xmin=-.5, ymin=-.5, typeset='latex')1493B = 1001494w = gaussian_primes(B)1495p1 = points(w, pointsize=10, zorder=10)1496p2 = points(list(cartesian_product_iterator([[0..B],[0..B]])), pointsize=1, color='grey', zorder=15)1497(p1 + p2).save("%s/gaussian_primes-%s.%s"%(dir,B,ext), aspect_ratio=1, frame=True, axes=False, typeset='latex')1498149915001501##############################################################1502# Random Walks1503##############################################################1504def random_walk(n):1505import random1506return stats.TimeSeries([random.choice([-1r,1r]) for _ in range(n)]).sums()15071508def random_walks(path, ext, B, n=1000, seed=1):1509set_random_seed(seed)1510path = path + '-%s'%B1511v = [random_walk(n) for i in range(B)]1512g = sum([z.plot(thickness=.3) for z in v])1513g.save(path + '.' + ext, fontsize=18, typeset='latex')1514s = sum([z.abs().vector()/B for z in v])1515avg = stats.TimeSeries(list(s))1516h = avg.plot() + plot(sqrt(2/pi)*sqrt(x), (0,g.xmax()), color='red', thickness=2)1517h.save(path + '-mean.' + ext, fontsize=18, typeset='latex')15181519def fig_random_walks(dir, ext):1520random_walks(dir + '/random_walks', ext, 3)1521random_walks(dir + '/random_walks', ext, 10)1522random_walks(dir + '/random_walks', ext, 100)1523random_walks(dir + '/random_walks', ext, 1000)152415251526##############################################################1527# Theta_C1528##############################################################15291530def plot_theta_C(C, xmax):1531theta = var('theta')1532T = SR(0)1533for q in prime_powers(C+1):1534if q > 1:1535p, n = factor(q)[0]1536T += 2 * p^(-n/2) * log(p) * cos(n*log(p)*theta)1537g = plot(T, 0, xmax)1538if g.ymax() > 10:1539g.ymax(10)1540ym = g.ymax()1541Z = []1542for y in zeta_zeros():1543if y > xmax: break1544Z.append(y)1545zeros = sum([arrow((x,ym),(x,0),rgbcolor='red',width=1.5,arrowsize=2)1546for i, x in enumerate(Z)])1547g += zeros1548g += plot(T.derivative(), 0, xmax, color='grey', alpha=.5)1549g.ymax(min(ym,10))1550g.ymin(max(-15,g.ymin()))1551return g15521553def fig_theta_C(dir, ext):1554def f(C,xmax):1555plot_theta_C(C,xmax).save(dir+'/theta_C-%s.%s'%(C,ext), fontsize=20, typeset='latex')1556f(3, 40)1557f(5, 40)1558f(10, 40)1559f(20, 40)1560f(100, 40)1561f(200, 40)1562f(500, 40)15631564def fig_theta_C_intro(dir, ext):1565theta = var('theta')1566def f(C):1567T = SR(0)1568for q in prime_powers(C+1):1569if q > 1:1570p, n = factor(q)[0]1571T += 2 * p^(-n/2) * log(p) * cos(n*log(p)*theta)1572return T1573h = f(3)1574plot(h, 5, 40).save("%s/theta_3_intro-1.%s"%(dir, ext), fontsize=18, typeset='latex')15751576roots = [(h.derivative().find_root(a,b),0) for a,b in [(5,7), (7,9), (10,12), (13,15), (16,17.8), (18,21), (21,24), (24.5,26), (27,30),1577(30, 33), (33,36), (36, 38), (38,42)]]15781579save(plot(h, 5, 40) + plot(h.derivative(),5,40,color='grey') + points(roots, color='red', pointsize=50, zorder=10),1580"%s/theta_3_intro-2.%s"%(dir, ext), fontsize=18)158115821583##############################################################1584# Cesaro Sum1585##############################################################15861587def fig_cesaro(dir, ext):1588theta = var('theta')1589def f(C):1590T = SR(0)1591for q in prime_powers(C+1):1592if q > 1:1593p, n = factor(q)[0]1594T += 2 * p^(-n/2) * log(p) * cos(n*log(p)*theta)1595return T1596k = f(5)^21597T = var('T')1598assume(T>0)1599g(T) = 1/T * k.integrate(theta, 0, T)1600h = plot(g, 0, 40, color='red') + plot(k,0,40)1601h.save('%s/cesaro.%s'%(dir, ext), fontsize=16, typeset='latex')160216031604##############################################################1605# Spacing of Zeros1606##############################################################16071608def fig_zero_spacing(dir, ext):1609v = zeta_zeros()16101611def rmod(a,b):1612return a - a//b *b16131614def f(per):1615per = float(per)1616w = [rmod(a,per) for a in v]1617return stats.TimeSeries(w).plot_histogram()16181619f(2*pi).save('%s/zero-spacing-mod2pi.%s'%(dir, ext), fontsize=24, typeset='latex')16201621f(1).save('%s/zero-spacing-mod1.%s'%(dir, ext), fontsize=24, typeset='latex')162216231624##############################################################1625# Staircase of the Riemann spectrum1626##############################################################16271628def fig_staircase_riemann_spectrum(dir, ext):1629v = [float(a) for a in zeta_zeros()]1630t = stats.TimeSeries(v)1631def zeta_pi(X): # not efficient -- but lets us use general plotting machinery, and is fast enough for a book illustration!1632return len(t.clip_remove(max=X))1633def g(n, curve=False, **kwds):1634p = plot(zeta_pi, 1,n,1635plot_points=1000,rgbcolor='red',1636fillcolor=(.9,.9,.9),fill=True, **kwds)1637if curve:1638T = var('T')1639p += plot(T/(2*pi) * log(T/(2*pi*e)), 1, n, thickness=.5)1640return p1641g(30, curve=False, thickness=3).save('%s/staircase-riemann-spectrum-30.%s'%(dir, ext), fontsize=18, typeset='latex')1642g(50, curve=False, thickness=3).save('%s/staircase-riemann-spectrum-50.%s'%(dir, ext), fontsize=18, typeset='latex')1643g(100, curve=True, thickness=3).save('%s/staircase-riemann-spectrum-100.%s'%(dir, ext), fontsize=18, typeset='latex')1644g(1000, curve=True, thickness=3).save('%s/staircase-riemann-spectrum-1000.%s'%(dir, ext), fontsize=18, typeset='latex')164516461647##############################################################1648# Distribution of Riemann Spectrum Gaps1649##############################################################16501651def fig_riemann_spectrum_gaps(dir, ext):1652t = stats.TimeSeries(zeta_zeros())1653g = t.diffs().plot_histogram(bins=500, normalize=False)1654g.save("%s/riemann_spectrum_gaps.%s"%(dir, ext), xmax=2.3, frame=True, gridlines=True, axes=False, fontsize=18, typeset='latex')1655165616571658##############################################################1659# Cesaro Sum of Li - Pi1660##############################################################16611662def prime_pi_time_series(B):1663"""1664Return the time series stats.TimeSeries([prime_pi(n) for n in [0..B-1]])1665but computed (vastly) more efficiently.1666"""1667x = 01668w = []1669pp = 01670for p in prime_range(B):1671w.extend([x]*(p-pp))1672pp = p1673x += 11674w.extend([x]*(B-pp))1675return stats.TimeSeries(w)16761677def running_average(v):1678"""1679Return the running average of the time series... i.e., the Cesaro sum.1680i.e., stats.TimeSeries([0]+[v[:i].mean() for i in range(1,len(v))]),1681but computed (vastly) more efficiently.1682"""1683s = v.sums()1684# now divide s[i] by i+1 for i>=1.1685for i in range(1,len(s)):1686s[i] /= i+11687return s16881689def fig_li_minus_pi(dir, ext):1690B = 2500001691v = prime_pi_time_series(B)1692from mpmath import li1693# This takes about 30 seconds...1694li_minus_pi = stats.TimeSeries([0,0] + [li(i,offset=True)-v[i] for i in range(2,B)])1695v2 = running_average(li_minus_pi)1696g = li_minus_pi.plot(plot_points=B) + v2.plot(color='red', plot_points=B) + plot(sqrt(2/pi)*sqrt(x/log(x)), (2, B), color='purple')1697g.save('%s/li-minus-pi-250000.%s'%(dir, ext), fontsize=18, ticks=[[10000, 100000, 250000], None], tick_formatter=['latex', None], typeset='latex')169816991700170117021703