Added QR decomposition tests (mostly a pretty thorough randomized test);
fixed error in `matrix-cols-orthogonal?' revealed by testing
This commit is contained in:
parent
f5fa93572d
commit
c8e3b45e38
|
@ -18,6 +18,7 @@
|
||||||
vandermonde-matrix)
|
vandermonde-matrix)
|
||||||
(except-in "private/matrix/matrix-basic.rkt"
|
(except-in "private/matrix/matrix-basic.rkt"
|
||||||
matrix-dot
|
matrix-dot
|
||||||
|
matrix-cos-angle
|
||||||
matrix-angle
|
matrix-angle
|
||||||
matrix-normalize
|
matrix-normalize
|
||||||
matrix-conjugate
|
matrix-conjugate
|
||||||
|
@ -70,6 +71,8 @@
|
||||||
[matrix-dot
|
[matrix-dot
|
||||||
(case-> ((Matrix Number) -> Nonnegative-Real)
|
(case-> ((Matrix Number) -> Nonnegative-Real)
|
||||||
((Matrix Number) (Matrix Number) -> Number))]
|
((Matrix Number) (Matrix Number) -> Number))]
|
||||||
|
[matrix-cos-angle
|
||||||
|
((Matrix Number) (Matrix Number) -> Number)]
|
||||||
[matrix-angle
|
[matrix-angle
|
||||||
((Matrix Number) (Matrix Number) -> Number)]
|
((Matrix Number) (Matrix Number) -> Number)]
|
||||||
[matrix-normalize
|
[matrix-normalize
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
matrix-inf-norm
|
matrix-inf-norm
|
||||||
matrix-norm
|
matrix-norm
|
||||||
matrix-dot
|
matrix-dot
|
||||||
|
matrix-cos-angle
|
||||||
matrix-angle
|
matrix-angle
|
||||||
matrix-normalize
|
matrix-normalize
|
||||||
;; Simple operators
|
;; Simple operators
|
||||||
|
@ -240,10 +241,15 @@
|
||||||
(λ: ([js : Indexes])
|
(λ: ([js : Indexes])
|
||||||
(* (aproc js) (conjugate (bproc js))))))]))
|
(* (aproc js) (conjugate (bproc js))))))]))
|
||||||
|
|
||||||
|
(: matrix-cos-angle (case-> ((Matrix Real) (Matrix Real) -> Real)
|
||||||
|
((Matrix Number) (Matrix Number) -> Number)))
|
||||||
|
(define (matrix-cos-angle M N)
|
||||||
|
(/ (matrix-dot M N) (* (matrix-2norm M) (matrix-2norm N))))
|
||||||
|
|
||||||
(: matrix-angle (case-> ((Matrix Real) (Matrix Real) -> Real)
|
(: matrix-angle (case-> ((Matrix Real) (Matrix Real) -> Real)
|
||||||
((Matrix Number) (Matrix Number) -> Number)))
|
((Matrix Number) (Matrix Number) -> Number)))
|
||||||
(define (matrix-angle M N)
|
(define (matrix-angle M N)
|
||||||
(acos (/ (matrix-dot M N) (* (matrix-2norm M) (matrix-2norm N)))))
|
(acos (matrix-cos-angle M N)))
|
||||||
|
|
||||||
(: matrix-normalize
|
(: matrix-normalize
|
||||||
(All (A) (case-> ((Matrix Real) -> (Matrix Real))
|
(All (A) (case-> ((Matrix Real) -> (Matrix Real))
|
||||||
|
@ -361,7 +367,7 @@
|
||||||
(matrix-map-cols (make-matrix-normalize p) M fail)]))
|
(matrix-map-cols (make-matrix-normalize p) M fail)]))
|
||||||
|
|
||||||
;; ===================================================================================================
|
;; ===================================================================================================
|
||||||
;; Robust predicates using entrywise norms
|
;; Approximate predicates using entrywise norms
|
||||||
|
|
||||||
(: matrix-zero? (case-> ((Matrix Number) -> Boolean)
|
(: matrix-zero? (case-> ((Matrix Number) -> Boolean)
|
||||||
((Matrix Number) Real -> Boolean)))
|
((Matrix Number) Real -> Boolean)))
|
||||||
|
@ -369,26 +375,26 @@
|
||||||
(cond [(negative? eps) (raise-argument-error 'matrix-zero? "Nonnegative-Real" 1 M eps)]
|
(cond [(negative? eps) (raise-argument-error 'matrix-zero? "Nonnegative-Real" 1 M eps)]
|
||||||
[else (<= (matrix-norm M +inf.0) eps)]))
|
[else (<= (matrix-norm M +inf.0) eps)]))
|
||||||
|
|
||||||
(: rows-orthogonal? ((Matrix Number) Nonnegative-Real -> Boolean))
|
(: pairwise-orthogonal? ((Listof (Matrix Number)) Nonnegative-Real -> Boolean))
|
||||||
(define (rows-orthogonal? M eps)
|
(define (pairwise-orthogonal? Ms eps)
|
||||||
(define rows (matrix->vector* M))
|
(define rows (list->vector Ms))
|
||||||
(define m (vector-length rows))
|
(define m (vector-length rows))
|
||||||
(let/ec: return : Boolean
|
(let/ec: return : Boolean
|
||||||
(for*: ([i0 (in-range m)] [i1 (in-range (fx+ i0 1) m)])
|
(for*: ([i0 (in-range m)] [i1 (in-range (fx+ i0 1) m)])
|
||||||
(define r0 (unsafe-vector-ref rows i0))
|
(define r0 (unsafe-vector-ref rows i0))
|
||||||
(define r1 (unsafe-vector-ref rows i1))
|
(define r1 (unsafe-vector-ref rows i1))
|
||||||
(when ((sqrt (magnitude (vector-dot r0 r1))) . >= . eps) (return #f)))
|
(when ((magnitude (matrix-cos-angle r0 r1)) . >= . eps) (return #f)))
|
||||||
#t))
|
#t))
|
||||||
|
|
||||||
(: matrix-rows-orthogonal? (case-> ((Matrix Number) -> Boolean)
|
(: matrix-rows-orthogonal? (case-> ((Matrix Number) -> Boolean)
|
||||||
((Matrix Number) Real -> Boolean)))
|
((Matrix Number) Real -> Boolean)))
|
||||||
(define (matrix-rows-orthogonal? M [eps (* 10 epsilon.0)])
|
(define (matrix-rows-orthogonal? M [eps (* 10 epsilon.0)])
|
||||||
(cond [(negative? eps) (raise-argument-error 'matrix-rows-orthogonal? "Nonnegative-Real" 1 M eps)]
|
(cond [(negative? eps) (raise-argument-error 'matrix-rows-orthogonal? "Nonnegative-Real" 1 M eps)]
|
||||||
[else (rows-orthogonal? M eps)]))
|
[else (pairwise-orthogonal? (matrix-rows M) eps)]))
|
||||||
|
|
||||||
|
|
||||||
(: matrix-cols-orthogonal? (case-> ((Matrix Number) -> Boolean)
|
(: matrix-cols-orthogonal? (case-> ((Matrix Number) -> Boolean)
|
||||||
((Matrix Number) Real -> Boolean)))
|
((Matrix Number) Real -> Boolean)))
|
||||||
(define (matrix-cols-orthogonal? M [eps (* 10 epsilon.0)])
|
(define (matrix-cols-orthogonal? M [eps (* 10 epsilon.0)])
|
||||||
(cond [(negative? eps) (raise-argument-error 'matrix-cols-orthogonal? "Nonnegative-Real" 1 M eps)]
|
(cond [(negative? eps) (raise-argument-error 'matrix-cols-orthogonal? "Nonnegative-Real" 1 M eps)]
|
||||||
[else (rows-orthogonal? (matrix-transpose M) eps)]))
|
[else (pairwise-orthogonal? (matrix-cols M) eps)]))
|
||||||
|
|
|
@ -930,14 +930,6 @@
|
||||||
[ 3/7 158/175 6/175]
|
[ 3/7 158/175 6/175]
|
||||||
[-2/7 6/35 -33/35 ]]))
|
[-2/7 6/35 -33/35 ]]))
|
||||||
|
|
||||||
(check-equal? (matrix-gram-schmidt (matrix [[12 -51 4]
|
|
||||||
[ 6 167 -68]
|
|
||||||
[-4 24 -41]])
|
|
||||||
#t)
|
|
||||||
(matrix [[ 6/7 -69/175 -58/175]
|
|
||||||
[ 3/7 158/175 6/175]
|
|
||||||
[-2/7 6/35 -33/35 ]]))
|
|
||||||
|
|
||||||
(check-equal? (matrix-gram-schmidt (matrix [[12 -51]
|
(check-equal? (matrix-gram-schmidt (matrix [[12 -51]
|
||||||
[ 6 167]
|
[ 6 167]
|
||||||
[-4 24]])
|
[-4 24]])
|
||||||
|
@ -955,36 +947,67 @@
|
||||||
;; ===================================================================================================
|
;; ===================================================================================================
|
||||||
;; QR decomposition
|
;; QR decomposition
|
||||||
|
|
||||||
(check-true (matrix-orthonormal? (matrix-q (index-array #(100 1)))))
|
(check-true (matrix-orthonormal? (matrix-q (index-array #(50 1)))))
|
||||||
|
|
||||||
|
(let-values ([(Q R) (matrix-qr (matrix [[12 -51 4]
|
||||||
|
[ 6 167 -68]
|
||||||
|
[-4 24 -41]]))])
|
||||||
|
(check-equal? Q (matrix [[ 6/7 -69/175 -58/175]
|
||||||
|
[ 3/7 158/175 6/175]
|
||||||
|
[-2/7 6/35 -33/35 ]]))
|
||||||
|
(check-equal? R (matrix [[14 21 -14]
|
||||||
|
[ 0 175 -70]
|
||||||
|
[ 0 0 35]])))
|
||||||
|
|
||||||
|
;; A particularly tricky test case used to demonstrate loss of orthogonality
|
||||||
|
;; QR has to generate a better Q than Gram-Schmidt alone (which fails this test)
|
||||||
|
(check-true (matrix-orthonormal?
|
||||||
|
(matrix-q (matrix [[0.70000 0.70711]
|
||||||
|
[0.70001 0.70711]]))))
|
||||||
|
|
||||||
|
;; Fuzz test the heck out of it: 100 matrices, random shape, random entries, sometimes rank-deficient
|
||||||
|
(for: ([i (in-range 100)])
|
||||||
|
(define m (+ 1 (random 10)))
|
||||||
|
(define n (+ 1 (random 10)))
|
||||||
|
(define M (random-matrix m n -3 4))
|
||||||
|
;; Full QR, real matrix
|
||||||
|
(let-values ([(Q R) (matrix-qr M #t)])
|
||||||
|
(check-true (matrix-orthonormal? Q)
|
||||||
|
(format "M = ~a Q = ~a" M Q))
|
||||||
|
(check-true (<= (matrix-relative-error (matrix* Q R) M)
|
||||||
|
(* 10 epsilon.0))))
|
||||||
|
;; Reduced QR, real matrix
|
||||||
|
(let-values ([(Q R) (matrix-qr M #f)])
|
||||||
|
(check-true (matrix-cols-orthogonal? Q)
|
||||||
|
(format "M = ~a Q = ~a" M Q))
|
||||||
|
(check-true (<= (matrix-relative-error (matrix* Q R) M)
|
||||||
|
(* 10 epsilon.0))))
|
||||||
|
(define N (random-matrix m n -3 4))
|
||||||
|
(define M+N (array-make-rectangular M N))
|
||||||
|
;; Full QR, complex matrix
|
||||||
|
(let-values ([(Q R) (matrix-qr M+N #t)])
|
||||||
|
(check-true (matrix-orthonormal? Q)
|
||||||
|
(format "M+N = ~a Q = ~a" M+N Q))
|
||||||
|
(check-true (<= (matrix-relative-error (matrix* Q R) M+N)
|
||||||
|
(* 10 epsilon.0))))
|
||||||
|
;; Reduced QR, complex matrix
|
||||||
|
(let-values ([(Q R) (matrix-qr M+N #f)])
|
||||||
|
(check-true (matrix-cols-orthogonal? Q)
|
||||||
|
(format "M+N = ~a Q = ~a" M+N Q))
|
||||||
|
(check-true (<= (matrix-relative-error (matrix* Q R) M+N)
|
||||||
|
(* 10 epsilon.0)))))
|
||||||
|
|
||||||
|
(for: ([M (in-list nonmatrices)])
|
||||||
|
(check-exn exn:fail:contract? (λ () (matrix-q M))))
|
||||||
|
|
||||||
#|
|
#|
|
||||||
;; ===================================================================================================
|
;; ===================================================================================================
|
||||||
;; Tests not yet converted to rackunit
|
;; Tests not yet converted to rackunit
|
||||||
|
|
||||||
;; A particularly tricky one used to demonstrate loss of orthogonality
|
|
||||||
(matrix-qr (matrix [[0.70000 0.70711]
|
|
||||||
[0.70001 0.70711]]))
|
|
||||||
|
|
||||||
(begin
|
(begin
|
||||||
|
|
||||||
(begin
|
(begin
|
||||||
"matrix-operations.rkt"
|
"matrix-operations.rkt"
|
||||||
#;
|
|
||||||
(list 'column-dimension
|
|
||||||
(= (column-dimension #(1 2 3)) 3)
|
|
||||||
(= (column-dimension (vector->matrix 1 2 #(1 2))) 1))
|
|
||||||
(let ([matrix: vector->matrix])
|
|
||||||
(list 'column-dot
|
|
||||||
(= (column-dot (col-matrix [1 2]) (col-matrix [1 2])) 5)
|
|
||||||
(= (column-dot (col-matrix [1 2]) (col-matrix [3 4])) 11)
|
|
||||||
(= (column-dot (col-matrix [3 4]) (col-matrix [3 4])) 25)
|
|
||||||
(= (column-dot (col-matrix [1 2 3]) (col-matrix [4 5 6]))
|
|
||||||
(+ (* 1 4) (* 2 5) (* 3 6)))
|
|
||||||
(= (column-dot (col-matrix [+3i +4i]) (col-matrix [+3i +4i]))
|
|
||||||
25)))
|
|
||||||
(let ([matrix: vector->matrix])
|
|
||||||
(list 'column-norm
|
|
||||||
(= (column-norm (col-matrix [2 4])) (sqrt 20))))
|
|
||||||
(list 'column-project
|
(list 'column-project
|
||||||
(equal? (column-project #(1 2 3) #(4 5 6)) (col-matrix [128/77 160/77 192/77]))
|
(equal? (column-project #(1 2 3) #(4 5 6)) (col-matrix [128/77 160/77 192/77]))
|
||||||
(equal? (column-project (col-matrix [1 2 3]) (col-matrix [2 4 3]))
|
(equal? (column-project (col-matrix [1 2 3]) (col-matrix [2 4 3]))
|
||||||
|
@ -1002,45 +1025,9 @@
|
||||||
(matrix-scale (col-matrix [-1 1 -1 1]) 1/2)
|
(matrix-scale (col-matrix [-1 1 -1 1]) 1/2)
|
||||||
(matrix-scale (col-matrix [ 1 -1 -1 1]) 1/2)))
|
(matrix-scale (col-matrix [ 1 -1 -1 1]) 1/2)))
|
||||||
(col-matrix [2 3 2 3])))
|
(col-matrix [2 3 2 3])))
|
||||||
(list 'gram-schmidt-orthogonal
|
|
||||||
(equal? (gram-schmidt-orthogonal (list #(3 1) #(2 2)))
|
|
||||||
(list (col-matrix [3 1]) (col-matrix [-2/5 6/5]))))
|
|
||||||
(list 'vector-normalize
|
|
||||||
(equal? (column-normalize #(3 4))
|
|
||||||
(col-matrix [3/5 4/5])))
|
|
||||||
(list 'gram-schmidt-orthonormal
|
|
||||||
(equal? (gram-schmidt-orthonormal (ann '(#(3 1) #(2 2)) (Listof (Column Number))))
|
|
||||||
(list (column-normalize #(3 1))
|
|
||||||
(column-normalize #(-2/5 6/5)))))
|
|
||||||
|
|
||||||
(list 'projection-on-subspace
|
(list 'projection-on-subspace
|
||||||
(equal? (projection-on-subspace #(1 2 3) '(#(2 4 3)))
|
(equal? (projection-on-subspace #(1 2 3) '(#(2 4 3)))
|
||||||
(matrix-scale (col-matrix [2 4 3]) 19/29)))
|
(matrix-scale (col-matrix [2 4 3]) 19/29)))
|
||||||
(list 'unit-vector
|
|
||||||
(equal? (unit-column 4 1) (col-matrix [0 1 0 0])))
|
|
||||||
(list 'matrix-qr
|
|
||||||
(let-values ([(Q R) (matrix-qr (matrix [[1 1] [0 1] [1 1]]))])
|
|
||||||
(equal? (list Q R)
|
|
||||||
(list (matrix [[0.7071067811865475 0]
|
|
||||||
[0 1]
|
|
||||||
[0.7071067811865475 0]])
|
|
||||||
(matrix [[1.414213562373095 1.414213562373095]
|
|
||||||
[0 1]]))))
|
|
||||||
(let ()
|
|
||||||
(define A (matrix [[1 2 3 4] [1 2 4 5] [1 2 5 6] [1 2 6 7]]))
|
|
||||||
(define-values (Q R) (matrix-qr A))
|
|
||||||
(equal? (list Q R)
|
|
||||||
(list
|
|
||||||
(vector->matrix
|
|
||||||
4 4 ((inst vector Number)
|
|
||||||
1/2 -0.6708203932499369 0.5477225575051662 -0.0
|
|
||||||
1/2 -0.22360679774997896 -0.7302967433402214 0.4082482904638629
|
|
||||||
1/2 0.22360679774997896 -0.18257418583505536 -0.8164965809277259
|
|
||||||
1/2 0.6708203932499369 0.3651483716701107 0.408248290463863))
|
|
||||||
(vector->matrix
|
|
||||||
4 4 ((inst vector Number)
|
|
||||||
2 4 9 11 0 0.0 2.23606797749979 2.23606797749979
|
|
||||||
0 0 0.0 4.440892098500626e-16 0 0 0 0.0))))))
|
|
||||||
#;
|
#;
|
||||||
(begin
|
(begin
|
||||||
"matrix-2d.rkt"
|
"matrix-2d.rkt"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user