暗号理論でも名前だけは聞く楕円曲線ですがこれをグラフに描けと言われても結構難しいですね。sympyのplot_implicitを使えば簡単にグラフにでき、さらにそのグラフをmatplotlibの中に取り込んで利用する方法を紹介します。
sympyのplot_implicitでグラフを描く
例題1: 楕円曲線$y^2=x^3-4x$のグラフを描け
plot_implicitは非常に便利で、関数を$f(x,y)=0$の形(陰関数 implicit function)で与えるとグラフを描いてくれるというものです。
例題では、楕円曲線$y^2=x^3-4x$の式を渡すだけで描いてくれます。
from sympy import var
from sympy.plotting import plot_implicit
import matplotlib.pyplot as plt
var('x,y')
size = 5
plt = plot_implicit(x**3-4*x-y**2, (x, -size, size), (y, -size, size))
楕円曲線のグラフとその上の点を表示
ただこれだと他のmatplotlibの機能と組み合わせて他の図形を重ね合したりすることが出来ません。そこでplot_implicitを使うけど描画せずにget_pointsで座標だけを取得してmatplotlibのfillを使って描きます。
この座標だけを取得する関数plot_fxyを以下のように定義します。
import sympy as sp
def plot_fxy(G,CX,CY):
p = sp.plot_implicit(G, (x,)+CX, (y,)+CY, show=False)[0].get_points()[0]
px, py = [], []
for (ix, iy) in p:
px.extend([ix.start, ix.start, ix.end, ix.end, None])
py.extend([iy.start, iy.end, iy.end, iy.start, None])
return (px, py)
これを使って次の例題を解きます
例題2: 楕円曲線$y^2=x^3+17$のグラフを描き次の5点(-2,3), (-1,4), (2,5),(4,9),(8,23)がこの曲線上にあることを確認せよ
ここではX, Y=[-30,30]のサイズのキャンバスがinit_subplot(コードは最後に添付)で定義されたとして以下のコードで描くことができます。
# 楕円曲線とその上の点
import matplotlib.pyplot as plt
sizex = 30
CX, CY = (-sizex, sizex), (-sizex, sizex)
ax = init_subplot(plt, CX, CY)
x, y = sp.symbols("x y")
ax.fill(*plot_fxy(x**3+17-y**2, CX, CY), facecolor='blue')
QP = [(-2,3),(-1,4),(2,5),(4,9),(8,23)]
for (qx,qy) in QP:
ax.plot(qx,qy, marker='o', ms="2.0", color='red')
ax.text(qx+1,qy-1, f"({qx},{qy})")
plt.show
色々な楕円曲線を描く
次は楕円曲線(Wikipedia)にある楕円曲線のカタログのようにヴァイエルシュトラスの標準形のaとbを変えたグラフを描いてみます。
# 楕円曲線のカタログ
sizex = 3
CX, CY = (-sizex, sizex), (-sizex, sizex)
am, bm = 4, 4
ax = init_subplot(plt, CX, CY, am, bm) # Create 4 x 4 subplot
x, y = sp.symbols("x y")
for c in range(am):
for r in range(bm):
a, b = r-2, c-1
ax[r,c].fill(*plot_fxy(x**3+a*x+b-y**2, CX, CY), facecolor='blue')
ax[r,c].set_title(f"** a={a}, b={b} **")
plt.show
これで楕円曲線をmatplotlibのsubplot上に描けるようになったので、次の(その2)楕円曲線の演算をグラフで表していきたいと思います。
(開発環境:Google Colab)
(添付)init_subplotのソースコード
def init_subplot(plt, cx, cy, nccol=1, ncrow=1):
if nccol*ncrow == 1:
fig, ax = plt.subplots(nccol, ncrow, figsize=(4,4))
plt.tick_params(labelsize="large")
ax.set_xlim(cx); ax.set_ylim(cy)
ax.set_aspect('equal')
ax.grid()
else:
fig, ax = plt.subplots(nccol, ncrow, figsize=(8,8))
plt.tick_params(labelsize="small")
plt.rcParams["font.size"] = 5
plt.tight_layout()
for c in range(nccol):
for r in range(ncrow):
ax[r,c].set_xlim(cx); ax[r,c].set_ylim(cy)
ax[r,c].set_aspect('equal')
return ax