Di bab Inverse Matriks, matriks singular ditandai dengan exception LinAlgError: baris yang merupakan kelipatan baris lain membuat np.linalg.inv menolak bekerja. Padahal di dunia nyata, data redundan justru sering muncul. Contohnya: pemilik warung mencatat paket menu di sebuah sistem kasir, lalu staf menambahkan entri ganda “paket ganda = 2 kali paket hemat”. Baris baru itu tidak membawa informasi tambahan, dan matriks sistem persamaan harga yang terbentuk menjadi singular.
Pseudo-inverse adalah generalisasi inverse yang tetap terdefinisi untuk matriks singular sekalipun. NumPy menyediakannya sebagai np.linalg.pinv, dan fungsi ini selalu mengembalikan hasil untuk matriks apa pun — persegi atau tidak, full-rank maupun rank-deficient. Bab ini membandingkan perilaku inv dan pinv, menguji sifat Moore-Penrose, memakai pinv sebagai mesin least squares, lalu menutup dengan ambang rcond dan hubungannya dengan SVD.
inv Menolak, pinv Tetap Menghitung #
Kita pakai matriks 2x2 dengan baris kedua tepat 2 kali baris pertama — pola entri ganda pada cerita di atas:
import numpy as np
A = np.array([[1.0, 1.0],
[2.0, 2.0]]) # baris kedua = 2 kali baris pertama
try:
np.linalg.inv(A)
except np.linalg.LinAlgError as e:
print("inv -> LinAlgError:", e)
print("pinv(A) =")
print(np.round(np.linalg.pinv(A), 2))Output yang Dihasilkan:
inv -> LinAlgError: Singular matrix
pinv(A) =
[[0.1 0.2]
[0.1 0.2]]Bedah baris demi baris:
- Baris 3-4 (
A): matriks 2x2 dengan rank 1 —np.linalg.matrix_rankpada bab Matrix Rank menilai matriks ini hanya membawa satu baris informasi bebas. - Baris 5-8 (
try/except):np.linalg.invgagal karena inverse memang tidak ada; fungsi tersebut melemparnp.linalg.LinAlgErrordengan pesan “Singular matrix”. Kita menangkap exception itu dan mencetaknya, sehingga program tetap berjalan dan output tetap terukur. - Baris 9-10 (
pinv):np.linalg.pinvbekerja lewat SVD dan tidak melempar exception untuk matriks singular. Hasilnya bukan inverse (yang tidak ada), melainkan matriks P = [[0.1, 0.2], [0.1, 0.2]] — pasangan terbaik yang mungkin untuk A.
Sifat Moore-Penrose #
Hasil pinv bukan matriks sebarang; hasilnya adalah Moore-Penrose inverse, satu-satunya matriks P yang memenuhi empat sifat sekaligus. Dua sifat utamanya bisa kita verifikasi langsung:
import numpy as np
A = np.array([[1.0, 1.0],
[2.0, 2.0]])
P = np.linalg.pinv(A)
print("rank A :", np.linalg.matrix_rank(A))
print("A @ P @ A == A:", np.allclose(A @ P @ A, A))
print("P @ A @ P == P:", np.allclose(P @ A @ P, P))
print("A @ P @ A =")
print(np.round(A @ P @ A, 10))Output yang Dihasilkan:
rank A : 1
A @ P @ A == A: True
P @ A @ P == P: True
A @ P @ A =
[[1. 1.]
[2. 2.]]Baris demi baris:
- Baris 6 (
P = ...): pseudo-inverse dihitung sekali lalu disimpan. - Baris 7: rank A = 1, konfirmasi bahwa matriks 2x2 ini rank-deficient.
- Baris 8: A @ P @ A kembali menjadi A — sifat pertama Moore-Penrose. Untuk matriks invertible, sifat ini dipenuhi inverse biasa; untuk matriks singular,
pinvmenghadirkan padanannya. - Baris 9: P @ A @ P kembali menjadi P — sifat kedua. Dua sifat lainnya menuntut A @ P dan P @ A simetris; keduanya juga berlaku untuk hasil
pinv.
pinv sebagai Mesin Least Squares #
Kembali ke kasus warung, kini dengan tiga catatan struk: paket hemat (1 nasi goreng + 1 es teh = Rp15000), entri gandanya (2 nasi goreng + 2 es teh = Rp31000 — kelipatan baris pertama, tetapi totalnya tidak konsisten karena salah ketik), dan paket keluarga (1 nasi goreng + 2 es teh = Rp19000). Sistem ini overdetermined, redundan, dan tidak konsisten sekaligus — kombinasi yang membuat solve maupun inv tidak relevan:
import numpy as np
A = np.array([[1.0, 1.0],
[2.0, 2.0],
[1.0, 2.0]])
b = np.array([15000.0, 31000.0, 19000.0])
x_pinv = np.linalg.pinv(A) @ b
x_lstsq = np.linalg.lstsq(A, b, rcond=None)[0]
print("pinv @ b :", x_pinv)
print("lstsq :", x_lstsq)
print("allclose :", np.allclose(x_pinv, x_lstsq))
print("galat maks:", np.abs(A @ x_pinv - b).max())Output yang Dihasilkan:
pinv @ b : [11800. 3600.]
lstsq : [11800. 3600.]
allclose : True
galat maks: 400.0- Baris 7 (
x_pinv = ...): mengalikan pseudo-inverse dengan b menghasilkan solusi least squares: harga kompromi nasi goreng Rp11800 dan es teh Rp3600. - Baris 8 (
x_lstsq = ...): fungsilstsqdari bab Least Squares menghasilkan angka yang sama. - Baris 10 (
allclose): kedua jalur identik karenapinvdanlstsqsama-sama bekerja di atas SVD. - Baris 11: galat terbesar Rp400 — kompromi yang tak terhindarkan dari struk yang saling bertentangan.
Ketika kolom A bebas linear, solusi least squares bersifat tunggal dan pinv(A) @ b memberikan jawaban yang sama dengan lstsq. Ketika kolom tidak bebas, pinv tetap menjawab dengan solusi least squares ber norma terkecil — keunggulan yang tidak dimiliki jalur inverse biasa.
Ambang rcond dan Hubungan dengan SVD #
Secara konsep, pinv menghitung SVD A = U S V^T (bab Singular Value Decomposition) lalu membentuk V · diag(1/s, dengan cutoff) · U^T: setiap nilai singular s yang berada di bawah ambang diperlakukan nol dan tidak dibalik. Ambang itu dikendalikan argumen rcond:
import numpy as np
A = np.array([[1.0, 0.0],
[0.0, 1e-10]])
print("rcond default:")
print(np.linalg.pinv(A))
print("rcond=1e-8:")
print(np.linalg.pinv(A, rcond=1e-8))Output yang Dihasilkan:
rcond default:
[[1.e+00 0.e+00]
[0.e+00 1.e+10]]
rcond=1e-8:
[[1. 0.]
[0. 0.]]- Baris 5-6 (
rcond default): nilai singular 1e-10 masih di atas ambang bawaan, sehingga dibalik menjadi 1e10. Angka sebesar itu adalah sinyal numerik buruk: pembalikan yang membengkak. - Baris 7-8 (
rcond=1e-8): nilai singular 1e-10 kini dianggap nol, sehingga kontribusinya ditiadakan dan P berisi 0 pada posisi tersebut.
Matriks dengan nilai singular yang sangat kecil disebut ill-conditioned; keputusan memotong atau membalik nilai singular itulah yang menentukan kestabilan hasil — tema utama bab Stabilitas Numerik.
Sebagai ringkasan:
| Situasi | np.linalg.inv | np.linalg.pinv |
|---|---|---|
| Matriks invertible | inverse persis | setara (dalam toleransi) |
| Matriks singular / rank-deficient | LinAlgError | tetap menghitung |
| Matriks tidak persegi | LinAlgError | tetap menghitung |