Making plots of modular forms¶
Inspired by the images and ideas of Elias Wegert, I thought it might be interesting to attempt to implement a version of his colorizing technique for complex functions in sage. The purpose is ultimately to revisit how one plots modular forms in the LMFDB (see lmfdb.org and click around to see various plots — some are good, others are less good). The challenge is that plotting a function from $\mathbb{C} \longrightarrow \mathbb{C}$ is that the graph is naturally 4-dimensional, and we are very bad at visualizing 4d things. In fact, we want to use only 2d to visualize it. A complex number $z = re^{i \theta}$ is determined by the magnitude ($r$) and the argument ($\theta$). Thus one typical approach to represent the value taken by a function $f$ at a point $z$ is to represent the magnitude of $f(z)$ in terms of the brightness, and to represent the argument in terms of color. For example, the typical complex space would then look like the following.P = complex_plot(x, [-2, 2], [-2, 2])
P.axes(False)
P.show()
complicated_func = x**2 * (x - 0.5) * (x - 0.5*i)
P = complex_plot(complicated_func, [-2, 2], [-2, 2])
P.axes(False)
P.show()
def normalize(complexval):
return 2*exp(2 * pi * i * complexval.abs())
f = lambda x: normalize(x)
P = complex_plot(f, [-2, 2], [-2, 2])
P.axes(False)
P.show()
f = lambda x: normalize(complicated_func(x))
P = complex_plot(f, [-2, 2], [-2, 2], plot_points=200)
P.axes(False)
P.show()
def normalize_log(complexval):
return 2*exp(2 * pi * i * log(complexval.abs()))
f = lambda x: normalize_log(x)
P = complex_plot(f, [-2, 2], [-2, 2], plot_points=200)
P.axes(False)
P.show()
f = lambda x: normalize_log(complicated_func(x))
P = complex_plot(f, [-2, 2], [-2, 2], plot_points=200)
P.axes(False)
P.show()
Alternative Idea¶
As an alternative, we might consider representing the argument in color and representing magnitude through contour lines. I've done this in a separate file calledmyplot.spyx
. Let's run this plotting function on these two examples
from myplot import ccomplex_plot
f = lambda x: x
P = ccomplex_plot(f, [-2, 2], [-2, 2], plot_points=400)
P.axes(False)
P.show()
P = ccomplex_plot(complicated_func, [-2, 2], [-2, 2], plot_points=400)
P.axes(False)
P.show()
Applied to modular forms¶
To try it out, let's plot a modular form.import numpy as np
DtoH = lambda x: (-CDF.0*x + 1)/(x - CDF.0)
Htoq = lambda x: exp(2*CDF.pi()*CDF.0*x)
Dtoq = lambda x: Htoq(DtoH(CDF(x)))
f = ModularForms(group=1, weight=12).newforms()[0].q_expansion(100).truncate()
P = ccomplex_plot(lambda x: +Infinity if abs(x) >= 0.99 else f(Dtoq(x)), (-1,1),(-1,1),
plot_points=500, aspect_ratio = 1, figsize=[5,5])
### slightly complicated nonsense
rgbs = P[0].rgb_data
inds = np.where(np.isnan(rgbs))
rgbs[inds] = 1.
### end complicated nonsense
P.axes(show=False)
P.show()
P = complex_plot(lambda x: +Infinity if abs(x) >= 0.99 else f(Dtoq(x)), (-1,1),(-1,1),
plot_points=500, aspect_ratio = 1, figsize=[5,5])
P.axes(False)
P.show()
ff = lambda x: normalize(Dtoq(x))
P = complex_plot(lambda x: +Infinity if abs(x) >= 0.99 else ff(x), (-1,1),(-1,1),
plot_points=200, aspect_ratio = 1, figsize=[5,5])
P.axes(False)
P.show()
ff = lambda x: normalize_log(Dtoq(x))
P = complex_plot(lambda x: +Infinity if abs(x) >= 0.99 else ff(x), (-1,1),(-1,1),
plot_points=200, aspect_ratio = 1, figsize=[5,5])
P.axes(False)
P.show()
Leave a comment
Info on how to comment
To make a comment, please send an email using the button below. Your email address won't be shared (unless you include it in the body of your comment). If you don't want your real name to be used next to your comment, please specify the name you would like to use. If you want your name to link to a particular url, include that as well.
bold, italics, and plain text are allowed in
comments. A reasonable subset of markdown is supported, including lists,
links, and fenced code blocks. In addition, math can be formatted using
$(inline math)$
or $$(your display equation)$$
.
Please use plaintext email when commenting. See Plaintext Email and Comments on this site for more. Note also that comments are expected to be open, considerate, and respectful.
Comments (3)
2023-01-01 PD
I see your plots here. Do you have an idea how to plot precisely this curve?
I am simulating few mathematical things (I am not a mathematician, only engineer) and would like to identify the coordinates of the points of the curve for making a comparison with tetraetion curves. I know how to start python scripts and was using sometime matplotlib (No clue of Sage).
No hurry in answering. This is a project I am doing in parallel to my life.
2023-01-02 DLD
I think the easiest (where easy here means writing the least new amount of code) would be to use a library or program that implements the Dedekind eta function or the Jacobi theta functions $\theta_i$. For example, these are all in sage.
In pure python, these are both done in mpmath.
As a cautionary note: evaluating $\lambda(\tau)$ is nontrivial when the imaginary part of tau is very small. Or equivalently, evaluating $\lambda(iy)$ is very hard when $y$ is very small. It's possible to be clever about this (using functional equations for lambda or for the theta functions or the eta function, which are all ultimately equivalently useful here). But many implementations won't do this by default — mpmath also doesn't do this cleverly.
Concretely, here is one implementation.
This makes a plot roughly identical to the wikipedia plot. I bet you could adjust it from there.
2023-01-02 PD
Thanks for the quick reply and your code! I was working already with few libs.