当初在上学的时候就被根系所震撼,恰好我们的《李代数和表示理论》课程也就上到根系那一章,前面几章的习题我还能动笔,后面的难以攀登,至今对根系知识充满敬畏之心,具体可以看作者写的习题解集。后来有缘在网上碰到一个用python绘制李代数E8的根系博客,甚是惊喜,便抽空复现了一下,效果如下。
第一步,安装依赖包cairocffi
pip install cairocffi
安装完这个包可能 还会报如下错误,解决方法就是下载 安装GTK应用程序,具体可以参考文献3,
import cairocffi as cairo
File "C:\Users\bowen\AppData\Roaming\Python\Python39\site-packages\cairocffi\__init__.py", line 47, in <module>
cairo = dlopen(
File "C:\Users\bowen\AppData\Roaming\Python\Python39\site-packages\cairocffi\__init__.py", line 44, in dlopen
raise OSError(error_message) # pragma: no cover
OSError: no library called "cairo-2" was found
no library called "cairo" was found
no library called "libcairo-2" was found
cannot load library 'libcairo.so.2': error 0x7e
cannot load library 'libcairo.2.dylib': error 0x7e
cannot load library 'libcairo-2.dll': error 0x7e
第二步,复制如下代码到IDE直接运行
from itertools import product, combinations
import cairocffi as cairo
import numpy as np
COLORS = [(0.894, 0.102, 0.11),
(0.216, 0.494, 0.72),
(0.302, 0.686, 0.29),
(0.596, 0.306, 0.639),
(1.0, 0.5, 0),
(1.0, 1.0, 0.2),
(0.65, 0.337, 0.157),
(0.97, 0.506, 0.75)]
# --- step one: compute all roots and edges ---
# There are 240 roots in the root system,
# mutiply them by a factor 2 to be handy for computations.
roots = []
# Roots of the form (+-1, +-1, 0, 0, 0, 0, 0, 0),
# signs can be chosen independently and the two non-zeros can be anywhere.
for i, j in combinations(range(8), 2):
for x, y in product([-2, 2], repeat=2):
v = np.zeros(8)
v[i] = x
v[j] = y
roots.append(v)
# Roots of the form 1/2 * (+-1, +-1, ..., +-1), signs can be chosen
# indenpendently except that there must be an even numer of -1s.
for v in product([-1, 1], repeat=8):
if sum(v) % 4 == 0:
roots.append(v)
roots = np.array(roots).astype(np.int64)
# Connect a root to its nearest neighbors,
# two roots are connected if and only if they form an angle of pi/3.
edges = []
for i, r in enumerate(roots):
for j, s in enumerate(roots[i+1:], i+1):
if np.sum((r - s)**2) == 8:
edges.append([i, j])
# --- Step two: compute a basis of the Coxeter plane ---
# A set of simple roots listed by rows of 'delta'
delta = np.array([[1, -1, 0, 0, 0, 0, 0, 0],
[0, 1, -1, 0, 0, 0, 0, 0],
[0, 0, 1, -1, 0, 0, 0, 0],
[0, 0, 0, 1, -1, 0, 0, 0],
[0, 0, 0, 0, 1, -1, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 0],
[-.5, -.5, -.5, -.5, -.5, -.5, -.5, -.5],
[0, 0, 0, 0, 0, 1, -1, 0]])
# Dynkin diagram of E8:
# 1---2---3---4---5---6---7
# |
# 8
# where vertex i is the i-th simple root.
# The cartan matrix:
cartan = np.dot(delta, delta.transpose())
# Now we split the simple roots into two disjoint sets I and J
# such that the simple roots in each set are pairwise orthogonal.
# It's obvious to see how to find such a partition given the
# Dynkin graph above: I = [1, 3, 5, 7] and J = [2, 4, 6, 8],
# since roots are not connected by an edge if and only if they are orthogonal.
# Then a basis of the Coxeter plane is given by
# u = sum (c[i] * delta[i]) for i in I,
# v = sum (c[j] * delta[j]) for j in J,
# where c is an eigenvector for the minimal
# eigenvalue of the Cartan matrix.
eigenvals, eigenvecs = np.linalg.eigh(cartan)
# The eigenvalues returned by eigh() are in ascending order
# and the eigenvectors are listed by columns.
c = eigenvecs[:, 0]
u = np.sum([c[i] * delta[i] for i in [0, 2, 4, 6]], axis=0)
v = np.sum([c[j] * delta[j] for j in [1, 3, 5, 7]], axis=0)
# Gram-Schimdt u, v and normalize them to unit vectors.
u /= np.linalg.norm(u)
v = v - np.dot(u, v) * u
v /= np.linalg.norm(v)
# --- step three: project to the Coxeter plane ---
roots_2d = [(np.dot(u, x), np.dot(v, x)) for x in roots]
# Sort these projected vertices by their modulus in the coxter plane,
# every successive 30 vertices form one ring in the resulting pattern,
# assign these 30 vertices a same color.
vertex_colors = np.zeros((len(roots), 3))
modulus = np.linalg.norm(roots_2d, axis=1)
ind_array = modulus.argsort()
for i in range(8):
for j in ind_array[30*i: 30*(i+1)]:
vertex_colors[j] = COLORS[i]
# --- step four: render to png image ---
image_size = 600
# The axis lie between [-extent, extent] x [-extent, extent]
extent = 2.4
linewidth = 0.0018
markersize = 0.05
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, image_size, image_size)
ctx = cairo.Context(surface)
ctx.scale(image_size/(extent*2.0), -image_size/(extent*2.0))
ctx.translate(extent, -extent)
ctx.set_source_rgb(1, 1, 1)
ctx.paint()
for i, j in edges:
x1, y1 = roots_2d[i]
x2, y2 = roots_2d[j]
ctx.set_source_rgb(0.2, 0.2, 0.2)
ctx.set_line_width(linewidth)
ctx.move_to(x1, y1)
ctx.line_to(x2, y2)
ctx.stroke()
for i in range(len(roots)):
x, y = roots_2d[i]
color = vertex_colors[i]
grad = cairo.RadialGradient(x, y, 0.0001, x, y, markersize)
grad.add_color_stop_rgb(0, *color)
grad.add_color_stop_rgb(1, *color/2)
ctx.set_source(grad)
ctx.arc(x, y, markersize, 0, 2*np.pi)
ctx.fill()
surface.write_to_png(r'D:\项目\E8\E8.png')
可能36行会报如下错误
roots = np.array(roots).astype(np.int)
File "C:\Users\bowen\AppData\Roaming\Python\Python39\site-packages\numpy\__init__.py", line 305, in __getattr__
raise AttributeError(__former_attrs__[attr])
AttributeError: module 'numpy' has no attribute 'int'.
`np.int` was a deprecated alias for the builtin `int`. To avoid this error in existing code, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.
The aliases was originally deprecated in NumPy 1.20; for more details and guidance see the original release note at:
https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
这是因为numpy包版本升级的原因,按照提醒如下改正过来便是,再次运行,便可以在本地生成一张精美的E8根系图,祝大家好运。
roots = np.array(roots).astype(np.int64)
1,《李代数和表示理论入门》习题集
https://download.csdn.net/download/zengbowengood/12889714
2,李代数E8 的根系 python绘图
https://www.cnblogs.com/bugutian/p/11098325.html
3,OSError: no library called “cairo” was found 怎么解决
https://blog.csdn.net/bz_xyz/article/details/104637487