6.13. พีชคณิตเชิงเส้น

พีชคณิตเชิงเส้นบนกล้องคืองานเมทริกซ์ขนาดเล็ก: การหมุน 3x3 ที่ผสมตัวอย่าง IMU เข้ากับเฟรมโลก เมทริกซ์ค่าคงที่ที่แก้ไขเลนส์ การอัปเดตความแปรปรวนร่วมของสถานะของตัวกรอง Kalman การปรับเส้นโค้งพหุนามที่สมการปกติออกมาเป็นการแก้ระบบเชิงเส้นขนาดเล็ก โมดูลย่อย numpy.linalg และ scipy.linalg ครอบคลุมขนาดนั้นพอดี

ฟังก์ชันอยู่ในสองโมดูล การดำเนินการผลคูณเมทริกซ์อยู่ที่ระดับบนสุดของ numpy การแยกตัวประกอบและผกผันเมทริกซ์อยู่ภายใต้ numpy.linalg ตัวแก้ระบบเชิงเส้นเฉพาะอยู่ภายใต้ scipy.linalg ตัวอย่างเช่น การคูณเมทริกซ์ขนาด 2x2:

from ulab import numpy as np

A = np.array([[1, 2], [3, 4]], dtype=np.float)
B = np.array([[5, 6], [7, 8]], dtype=np.float)
np.dot(A, B)
# array([[19.0, 22.0],
#        [43.0, 50.0]])

6.13.1. สิ่งที่มีให้ใช้งาน

  • dot() -- ผลคูณเมทริกซ์หรือเวกเตอร์

  • cross() -- ผลคูณเชิงขวางของเวกเตอร์ 3 มิติ

  • trace() -- ผลรวมของเส้นทแยงมุม

  • inv() -- ผกผันเมทริกซ์

  • det() -- ดีเทอร์มิแนนต์

  • cholesky() -- การแยกตัวประกอบ Cholesky (อินพุตที่สมมาตรและบวกแน่นอน)

  • eig() -- ค่าลักษณะเฉพาะและเวกเตอร์ลักษณะเฉพาะของเมทริกซ์สมมาตรจริง

  • norm() -- ค่า 2-norm ของเวกเตอร์หรือเมทริกซ์

  • qr() -- การแยกตัวประกอบ QR ด้วย mode='reduced' (ค่าเริ่มต้น) หรือ mode='complete'

  • solve_triangular() -- แก้ A @ x = b เมื่อ A เป็นเมทริกซ์สามเหลี่ยม

  • cho_solve() -- แก้ A @ x = b เมื่อให้ตัวประกอบ Cholesky ของ A

6.13.2. dot, cross, trace

dot() คือวิธีการเขียนการคูณเมทริกซ์บนกล้อง ตัวดำเนินการ @ ที่ numpy บนเดสก์ท็อปมีให้สำหรับงานเดียวกันไม่ได้ถูกนำมาใช้บน ndarray ดังนั้นทุกการคูณเมทริกซ์-เวกเตอร์ เมทริกซ์-เมทริกซ์ และดอทโพรดักต์ของเวกเตอร์จึงผ่านการเรียก dot()

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.dot(a, b)             # 32.0 (scalar product)
np.cross(a, b)           # array([-3.0, 6.0, -3.0])

m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
np.dot(m, a)             # matrix-vector product
np.dot(m, m)             # matrix-matrix product
np.trace(m)              # 1 + 5 + 9 = 15.0

ผลลัพธ์ของ dot() มี dtype เป็น float เสมอ cross() คือผลคูณเชิงขวางของเวกเตอร์ 3 มิติสองตัว trace() คือผลรวมของเส้นทแยงมุมหลักของเมทริกซ์สี่เหลี่ยมจัตุรัส

6.13.3. inv และ det

m = np.array([[1, 2, 3, 4],
              [4, 5, 6, 4],
              [7, 9, 9, 4],
              [3, 4, 5, 6]])
print(np.linalg.inv(m))
print(np.linalg.det(m))

ผกผันถูกคำนวณโดยการกำจัดแบบ Gauss-Jordan ดังนั้น inv() จึง raise ValueError เมื่อเมทริกซ์เป็น singular (รายการในเส้นทแยงมุมกลายเป็นศูนย์ระหว่างการกำจัด) ต้นทุน RAM ประมาณสองเท่าขนาดของอินพุต

ดีเทอร์มิแนนต์นำการกำจัดเดิมมาใช้ซ้ำ -- เวลาทำงานโดยพื้นฐานแล้วเหมือนกับผกผัน

เมื่อเป้าหมายของแอปพลิเคชันคือการ แก้ ระบบเชิงเส้น อย่าผกผันและคูณ -- ใช้ตัวแก้เฉพาะด้านล่างแทน ทั้งคู่เร็วกว่าและมีพฤติกรรมดีกว่าทางตัวเลข

6.13.4. cholesky

สำหรับเมทริกซ์ A ที่สมมาตรและบวกแน่นอน cholesky() คืนค่า L สามเหลี่ยมล่างที่ A = L @ L.T

a = np.array([[25, 15, -5],
              [15, 18,  0],
              [-5,  0, 11]])
L = np.linalg.cholesky(a)

หากอินพุตไม่เป็นบวกแน่นอนหรือไม่สมมาตร จะ raise ValueError

ตัวประกอบ Cholesky ใช้งานครึ่งหนึ่งของการแยกตัวประกอบ LU และเป็นจุดเริ่มต้นที่ถูกต้องสำหรับปัญหาที่รู้ว่าเมทริกซ์เป็นสมมาตรบวกแน่นอน (การอัปเดตความแปรปรวนร่วม สมการปกติจากการปรับกำลังสองน้อยที่สุด)

6.13.5. eig

eig() ทำงานได้เฉพาะกับเมทริกซ์ สมมาตรจริง เท่านั้น เมทริกซ์ที่ไม่สมมาตรจะ raise ValueError มันคืนค่า tuple 2 ค่า (eigenvalues, eigenvectors)

a = np.array([[1, 2, 1, 4],
              [2, 5, 3, 5],
              [1, 3, 6, 1],
              [4, 5, 1, 7]], dtype=np.uint8)
x, y = np.linalg.eig(a)

หมายเหตุ:

  • ค่าลักษณะเฉพาะกลับมาโดยไม่มีลำดับเฉพาะ ใช้ sort() (และการจัดเรียงเดิมกับเวกเตอร์ลักษณะเฉพาะผ่าน argsort()) เมื่อลำดับที่เรียงแล้วมีความสำคัญ

  • เวกเตอร์ลักษณะเฉพาะมีความเป็นเอกลักษณ์เฉพาะถึงสเกลาร์ที่ไม่ใช่ศูนย์ ดังนั้น เครื่องหมาย ของเวกเตอร์ลักษณะเฉพาะแต่ละตัวจึงไม่ถูกกำหนดอย่างเป็นเอกลักษณ์ การรันสองครั้งที่ถูกต้องอาจสร้างเวกเตอร์ที่มีเครื่องหมายตรงข้าม ซึ่งไม่เป็นอันตราย

6.13.6. norm

ค่า Euclidean (Frobenius) norm ของเวกเตอร์หรือเมทริกซ์:

v = np.array([1, 2, 3, 4, 5])
m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

np.linalg.norm(v)        # 7.416...
np.linalg.norm(m)        # 16.881...

คีย์เวิร์ด axis= ที่เป็นทางเลือกนำค่า norm ตามแกนเดียวแทนที่จะเป็นทั้งอาร์เรย์

6.13.7. qr

qr() แยกตัวประกอบเมทริกซ์สี่เหลี่ยมผืนผ้า A (รูปร่าง (M, N)) ออกเป็น Q ที่ orthonormal และ R สามเหลี่ยมบน ที่ A == Q @ R

A = np.arange(6).reshape((3, 2))

q, r = np.linalg.qr(A)
# mode='reduced' (default): q is (3, 2), r is (2, 2)

q, r = np.linalg.qr(A, mode='complete')
# q is (3, 3), r is (3, 2)

การแยกตัวประกอบถูกนำมาใช้ผ่านการหมุน Givens ต่อเนื่อง เหมาะสำหรับปัญหากำลังสองน้อยที่สุดที่เมทริกซ์ไม่สมมาตร

6.13.8. การแก้ระบบ

ตัวแก้เฉพาะสองตัวภายใต้ ulab.scipy.linalg ทั้งคู่เร็วกว่าและแม่นยำกว่า np.dot(np.linalg.inv(A), b):

  • solve_triangular(a, b, lower=False)() -- แก้ a @ x = b โดยสมมติว่า a เป็นเมทริกซ์สามเหลี่ยม:

    A = np.array([[3, 0, 0, 0],
                  [2, 1, 0, 0],
                  [1, 0, 1, 0],
                  [1, 2, 1, 8]])
    b = np.array([4, 2, 4, 2])
    x = sp.linalg.solve_triangular(A, b, lower=True)
    
  • cho_solve(L, b)() -- เมื่อให้ตัวประกอบ Cholesky L แก้ A @ x = b โดยที่ A = L @ L.T

    L = np.linalg.cholesky(A)
    x = sp.linalg.cho_solve(L, b)
    

ใช้ตัวแก้เหล่านี้แทนการผกผันเสมอเมื่อโครงสร้างของ A อนุญาต -- ประหยัดงานการกำจัด และ การผกผันที่ชัดเจน

6.13.9. การแก้ระบบเชิงเส้นขนาดเล็ก

A = np.array([[3, 0, 1, 1],
              [0, 1, 0, 2],
              [1, 0, 1, 1],
              [1, 2, 1, 8]])
b = np.array([4, 2, 4, 2])

x = np.dot(np.linalg.inv(A), b)
print(x)
print(np.dot(A, x))         # should equal b

ปัญหาเดิมสามารถแสดงได้โดยการแยกตัวประกอบ A และเรียกตัวแก้ที่เหมาะสม -- เร็วกว่าและแม่นยำกว่าเมื่อ A มีโครงสร้างที่ถูกต้อง

สำหรับการอ้างอิงระดับอาร์กิวเมนต์ครบถ้วน ดูที่ numpy.linalg --- รูทีนพีชคณิตเชิงเส้น และ scipy.linalg --- รูทีนพีชคณิตเชิงเส้น