Sejauh ini kita memakai fungsi berbeda untuk tiap kebutuhan: np.dot untuk dot product, np.outer untuk outer product, M.T untuk transpose, @ untuk perkalian matriks, dan sum(axis=...) untuk penjumlahan per axis. Padahal semuanya satu keluarga: kalikan elemen pada sumbu tertentu, lalu jumlahkan hasilnya. np.einsum (Einstein summation) menyatukan semuanya dalam satu bahasa kecil bernama notasi subscript — satu sintaks untuk banyak operasi.
Membaca Notasi Subscript #
Aturannya empat dan seluruh contoh bab ini mengikutinya:
- Setiap operand diberi subscript — daftar huruf, satu huruf per sumbu — dipisah koma:
np.einsum("i,i->", a, b)berarti operand pertama berlabelidan operand kedua jugai. - Setelah panah
->tertulis subscript output: susunan sumbu pada hasil. - Huruf yang muncul di input tetapi tidak di output mengalami kontraksi: sepanjang sumbu itu elemen dikalikan lalu dijumlahkan.
- Huruf yang sama pada dua operand memaksa perkalian elemen-demi-elemen pada sumbu berlabel itu.
Dua contoh pembuka pada vektor:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.einsum("i,i->", a, b))
print(np.array_equal(np.einsum("i,i->", a, b), np.dot(a, b)))
print(np.einsum("i->", a))
print(np.array_equal(np.einsum("i->", a), a.sum()))Output yang Dihasilkan:
32
True
6
TrueBedah baris demi baris:
- Baris 5 (
np.einsum("i,i->", a, b)): hurufisama pada kedua operand →a[k]·b[k]untuk setiapk;itidak ada di kanan panah → seluruh hasil dijumlahkan. Nilainya32, sama dengannp.dot(a, b)(Chapter Dot Product). - Baris 6 (
np.array_equal(...)):True— einsum dannp.dotmenghasilkan nilai yang identik. - Baris 7 (
np.einsum("i->", a)): tanpa operand kedua;idibuang di output sehingga cukup penjumlahan total — setaraa.sum(). - Baris 8:
True.
Satu Operand: Transpose dan Penjumlahan per Baris #
Subscript satu operand cukup untuk operasi penyusunan ulang dan penjumlahan:
import numpy as np
M = np.array([[1, 2, 3],
[4, 5, 6]])
print(np.einsum("ij->ji", M))
print(np.array_equal(np.einsum("ij->ji", M), M.T))
print(np.einsum("ij->i", M))
print(np.array_equal(np.einsum("ij->i", M), M.sum(axis=1)))Output yang Dihasilkan:
[[1 4]
[2 5]
[3 6]]
True
[ 6 15]
TrueBedah baris demi baris:
- Baris 5 (
np.einsum("ij->ji", M)): sumbu output dibaca terbalik —M[i, j]ditaruh pada posisi(j, i). Itu definisi transpose; hasilnya identik denganM.T(Chapter transpose). - Baris 6:
Truemenegaskan kesamaan nilai denganM.T. - Baris 7 (
np.einsum("ij->i", M)): hurufjhilang di output → sepanjangjelemen dijumlahkan → jumlah per baris, identik denganM.sum(axis=1)(Chapter Operasi Berdasarkan Axis). - Baris 8:
True.
Dua Operand: Outer Product dan Perkalian Matriks #
Dengan dua operand, notasi subscript bahkan menggantikan perkalian matriks:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.einsum("i,j->ij", a, b))
print(np.array_equal(np.einsum("i,j->ij", a, b), np.outer(a, b)))
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
print(np.einsum("ij,jk->ik", A, B))
print(np.array_equal(np.einsum("ij,jk->ik", A, B), A @ B))Output yang Dihasilkan:
[[ 4 5 6]
[ 8 10 12]
[12 15 18]]
True
[[19 22]
[43 50]]
TrueBedah baris demi baris:
- Baris 5 (
np.einsum("i,j->ij", a, b)):idanjberbeda — tidak ada kontraksi; setiap pasangana[i]·b[j]dipertahankan pada grid(3, 3)→ outer product (Chapter Inner dan Outer Product). - Baris 6:
True— identik dengannp.outer(a, b). - Baris 12 (
np.einsum("ij,jk->ik", A, B)):jmuncul di kedua operand dan hilang di output →A[i, j]·B[j, k]dijumlahkan sepanjangj. Itu persis definisiC[i, k] = Σj A[i, j]·B[j, k], yaituA @ B(Chapter Matrix Multiplication). - Baris 13:
True.
Tabel Ringkasan Notasi #
Tabel berikut merangkum notasi yang dibahas beserta padanan eksplisitnya:
| Notasi | Operasi | Setara dengan |
|---|---|---|
"i,i->" | dot product | np.dot(a, b) |
"i->" | penjumlahan total | a.sum() |
"i,i->i" | perkalian elemen | a * b |
"i,j->ij" | outer product | np.outer(a, b) |
"ij->ji" | transpose | M.T |
"ij->i" | jumlah per baris | M.sum(axis=1) |
"ii->i" | diagonal | M.diagonal() |
"ij,jk->ik" | perkalian matriks | A @ B |
Notasi "ii->i", "i,i->i", dan "ij->" belum kita uji — blok berikut melengkapinya:
import numpy as np
M = np.array([[1, 2],
[3, 4]])
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.einsum("ii->i", M))
print(np.array_equal(np.einsum("ii->i", M), M.diagonal()))
print(np.einsum("i,i->i", a, b))
print(np.array_equal(np.einsum("i,i->i", a, b), a * b))
print(np.einsum("ij->", M))Output yang Dihasilkan:
[1 4]
True
[ 4 10 18]
True
10Bedah baris demi baris:
- Baris 7 (
np.einsum("ii->i", M)): huruf yang sama muncul dua kali pada satu operand → ambil elemen-diagonalM[0,0]danM[1,1]→[1 4], identik denganM.diagonal()(Chapter Diagonal dan Trace). - Baris 9 (
np.einsum("i,i->i", a, b)):isama di kedua operand tetapi tetap tertulis di output → perkalian elemen-demi-elemen tanpa penjumlahan, identik dengana * b. - Baris 11 (
np.einsum("ij->", M)): kedua sumbu dikontraksi → penjumlahan seluruh elemen, identik denganM.sum().