uncomplicate / neanderthal

Fast Clojure Matrix Library
http://neanderthal.uncomplicate.org
Eclipse Public License 1.0
1.06k stars 56 forks source link

Display of floats in Neanderthal #39

Closed lungsi closed 6 years ago

lungsi commented 6 years ago

Is there a way to control the display of significant digits in Neanderthal?

For example when I run:

;;; Calculate x in x = inverseA b
;; create matrix A 2x2
( def A ( dge 2 2 [3.8 1.2 7.2 -0.9] ) )
;; create vector b 2x1
( def b ( dv 16.5 -22.1 ) )
;; compute inverse of A and store the result in inv-A
( def inv-A ( tri ( trf A ) ) )
;; compute x
( mv inv-A b )

I get x to be,

#RealBlockVector[double, n:2, offset: 0, stride: 1]
[ -11.96 8.61 ]

If I do the example in Numpy

import numpy as np
from numpy.linalg import inv
A = np.array([[3.8, 7.2], [1.3, -0.9]])
b = np.array([16.5, -22.1])
inv_A = inv(A)

inv_A.dot(b) returns

array([-11.28873239,   8.24960876])

In Matlab you can control the display of floating-point numbers as

% for displaying 5-digit floating point
format short e
% for displaying 15-digit floating point
format long

With Neanderthal

#RealBlockVector[double, n:2, offset: 0, stride: 1]
[ -11.96 8.61 ]

My HP-15c gives the output: -11.2887 and 8.2496.

Is this just a rounding-off display issue while running Neanderthal? Its output [ -11.96 8.61 ] is significantly different from the actual answer [-11.2887 8.2496].

The bbsolute error is [0.6713 0.3604]. Relative error is [-0.0595 -0.0437].

blueberry commented 6 years ago

If you take a closer look, you'll see that you incidentally made a typo in the data you feed to the python (1.3 instead of 1.2 in matrix A). With the same numbers, Neanderthal gives the same results (with a bit more precision). Here's the output from my REPL:

user> (def a (dge 2 2 [3.8 1.3 7.2 -0.9]))
#'user/a
user> a
#RealGEMatrix[double, mxn:2x2, layout:column, offset:0]
   ▥       ↓       ↓       ┓    
   →       3.80    7.20         
   →       1.30   -0.90         
   ┗                       ┛    
user> (def inv-a (tri (trf a)))
#'user/inv-a
user> inv-a
#RealGEMatrix[double, mxn:2x2, layout:column, offset:0]
   ▥       ↓       ↓       ┓    
   →       0.07    0.56         
   →       0.10   -0.30         
   ┗                       ┛    
user> (def id (mm a inv-a))
#'user/id
user> id
#RealGEMatrix[double, mxn:2x2, layout:column, offset:0]
   ▥       ↓       ↓       ┓    
   →       1.00   -0.00         
   →      -0.00    1.00         
   ┗                       ┛    
user> (map seq id)
((1.0 -4.163336342344337E-17) (-4.440892098500626E-16 1.0))
user> (def b (dv 16.5 -22.1))
#'user/b
user> (mv inv-a b)
#RealBlockVector[double, n:2, offset: 0, stride:1]
[ -11.29    8.25 ]
user> (def ab (mv inv-a b))
#'user/ab
user> (seq ab)
(-11.2887323943662 8.249608763693272)
user> 

There you can see how you can easily get all significant numbers printed out by turning data to sequences. If you want to change the display of neanderthal matrices, you can supply different implementation of Clojure's print-method for the class you're interested in. Of course, the rounding happens only in printing, for the obvious reason that printing more digits quickly becomes unreadable, and the matrix printout mostly serves for quick visual inspection by a human.