Skip to content

Commit affe515

Browse files
bdeketsamth
authored andcommitted
acos/asin and matrix-(cos-)-angle
* restrict acos/asin for corrected type (samth) * improve matrix-cos-angle so * Real values are between -1 and 1 * Complex values have a magnitude at most (flnext 1.) * prevent early under/overflow * rescale matrix with inf values to a matrix of zero and ones (so it works similar to angle) * improve matrix-angle so type can be preserved * For real values use flacos * ok since Real values are now guaranteed to be within -1 to 1
1 parent 306c83c commit affe515

File tree

2 files changed

+99
-7
lines changed

2 files changed

+99
-7
lines changed

math-lib/math/private/matrix/matrix-basic.rkt

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,73 @@
309309
((Matrix Real) (Matrix Real) -> Real)
310310
((Matrix Float-Complex) (Matrix Float-Complex) -> Float-Complex)
311311
((Matrix Number) (Matrix Number) -> Number)))
312-
(define (matrix-cos-angle M N)
313-
(/ (matrix-dot M N) (* (matrix-2norm M) (matrix-2norm N))))
314-
315-
(: matrix-angle (case-> ((Matrix Float-Complex) (Matrix Float-Complex) -> Float-Complex)
312+
(define matrix-cos-angle
313+
(let ()
314+
(define (mag² [x : Number]) (if (real? x) (sqr x) (+ (sqr (real-part x)) (sqr (imag-part x)))))
315+
(define (inf=>1 [x : Real]) : Flonum (if (eq? x +inf.0) 1. (if (eq? x -inf.0) -1. 0.)))
316+
(: cinf=>1 (case-> (-> Real Flonum)
317+
(-> Float-Complex Float-Complex)
318+
(-> Number Number)))
319+
(define (cinf=>1 x)
320+
(if (real? x)
321+
(inf=>1 x)
322+
(make-flrectangular (inf=>1 (real-part x))
323+
(inf=>1 (imag-part x)))))
324+
(: unit-bound (case-> (-> Flonum Flonum)
325+
(-> Real Real)))
326+
(define (unit-bound x)
327+
(if (flonum? x)
328+
(flmin (flmax -1. x) 1.)
329+
(min (max -1 x) 1)))
330+
(: inner (case-> ((Matrix Flonum) (Matrix Flonum) -> Flonum)
331+
((Matrix Real) (Matrix Real) -> Real)
332+
((Matrix Float-Complex) (Matrix Float-Complex) -> Float-Complex)
333+
((Matrix Number) (Matrix Number) -> Number)))
334+
(define (inner A* B*)
335+
(define nA (sqrt (array-all-sum (inline-array-map mag² A*))))
336+
(define nB (sqrt (array-all-sum (inline-array-map mag² B*))))
337+
338+
(define result (/ (matrix-dot A* B*) (* nA nB)))
339+
340+
(if (real? result)
341+
(unit-bound result)
342+
(make-rectangular (unit-bound (real-part result))
343+
(unit-bound (imag-part result)))))
344+
(λ (A B)
345+
(define mA (array-strict (inline-array-map nonstupid-magnitude A)))
346+
(define mB (array-strict (inline-array-map nonstupid-magnitude B)))
347+
(define mxA (array-all-max mA))
348+
(define mxB (array-all-max mB))
349+
350+
(cond
351+
[(and (rational? mxA) (positive? mxA)
352+
(rational? mxB) (positive? mxB))
353+
(define A* (inline-array-map (λ (x) (/ x mxA)) A))
354+
(define B* (inline-array-map (λ (x) (/ x mxB)) B))
355+
356+
(inner A* B*)]
357+
[(or (nan? mxA) (nan? mxB) (= 0 mxA) (= 0 mxB))
358+
(/ (matrix-dot A B) (* mxA mxB))]
359+
[else
360+
(define A* (if (rational? mxA)
361+
(inline-array-map (λ (x) (/ x mxA)) A)
362+
(array-strict (inline-array-map cinf=>1 A))))
363+
(define B* (if (rational? mxB)
364+
(inline-array-map (λ (x) (/ x mxB)) B)
365+
(array-strict (inline-array-map cinf=>1 B))))
366+
(inner A* B*)]))))
367+
368+
(: matrix-angle (case-> ((Matrix Flonum) (Matrix Flonum) -> Flonum)
369+
((Matrix Real) (Matrix Real) -> Real)
370+
((Matrix Float-Complex) (Matrix Float-Complex) -> Float-Complex)
316371
((Matrix Number) (Matrix Number) -> Number)))
317-
(define (matrix-angle M N)
318-
(acos (matrix-cos-angle M N)))
372+
(define (matrix-angle A B)
373+
(define ca (matrix-cos-angle A B))
374+
(cond
375+
[(flonum? ca) (flacos ca)]
376+
[(real? ca)
377+
(if (eq? ca 1) 0 (flacos (fl ca)))]
378+
[else (acos ca)]))
319379

320380
(: matrix-normalize
321381
(All (A) (case-> ((Matrix Flonum) -> (Matrix Flonum))

math-test/math/tests/matrix-tests.rkt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,39 @@
661661
(check-exn exn:fail:contract? (λ () (matrix-dot a (matrix [[1]]))))
662662
(check-exn exn:fail:contract? (λ () (matrix-dot (matrix [[1]]) a))))
663663

664-
;; TODO: matrix-angle
664+
;; matrix-angle
665+
;; guarantee that matrix-cos-angle on real is in [-1 .. 1] except for zero?
666+
(for: ([i (in-range 10)])
667+
(define n (+ 1 (random 3)))
668+
(define m (+ 1 (random 4)))
669+
(define A (random-matrix n m))
670+
(define B (random-matrix n m))
671+
(define ca (matrix-cos-angle A B))
672+
(check-true (or (and (rational? ca) (<= ca 1.))
673+
(matrix-zero? A) (matrix-zero? B))))
674+
(let ([A (matrix [[-1.65e+145 -9.55e+152]])]
675+
[B (matrix [[-2.05e-130 -1.88e-122]])])
676+
(check-true (<= (abs (matrix-cos-angle A B)) 1.)))
677+
;; check matrix-cos-angle doesn't overflow easily
678+
(let ([A (matrix [[-1.20e-21+8.16e+173i -3.18e+280-4.15e+275i]])]
679+
[B (matrix [[-2.51e-270-4.38e-68i -1.65e+232+3.06e+227i]])])
680+
(define ca (matrix-cos-angle A B))
681+
;; 0.9999999995008538+3.159576900273938e-05i
682+
(check-true (<= (magnitude ca) (flnext 1.))))
683+
(let ([A (matrix-scale (matrix [[1 1]]) 1e270+3.i)]
684+
[B (matrix-scale (matrix [[9 0]]) 1e270+3.i)])
685+
(define ca (matrix-cos-angle A B))
686+
;; 0.9999999995008538+3.159576900273938e-05i
687+
(check-true (<= (magnitude (- ca (make-rectangular (sqrt 1/2) 0.))) epsilon.0)))
688+
;; check matrix-cos-angle treats axes with inf as principal axes
689+
(check-equal? (matrix-cos-angle (matrix [[1 5] [2 9]])
690+
(matrix [[2 8] [3 -inf.0]]))
691+
(matrix-cos-angle (matrix [[1 5] [2 9]])
692+
(matrix [[0. 0.] [0. -1.0]])))
693+
(check-equal? (matrix-cos-angle (matrix [[2 9]])
694+
(matrix [[3 -8+inf.0i]]))
695+
(matrix-cos-angle (matrix [[2 9]])
696+
(matrix [[0. -0+1.0i]])))
665697

666698
;; TODO: matrix-normalize
667699

0 commit comments

Comments
 (0)