[racket] unit test for close enough values

From: Neil Toronto (neil.toronto at gmail.com)
Date: Sat Dec 21 19:25:23 EST 2013

You've got yourself a tricky problem, here.

The accepted way to compare how close two vectors or matrices are is to 
compute the norm of their difference - basically, the distance between 
them. The functions `matrix-absolute-error' and `matrix-relative-error' 
in `math/matrix' do that, and handle +inf.0, +nan.0 entries correctly. 
You can use `->col-matrix' to convert vectors to matrices; i.e.

     (matrix-relative-error (->col-matrix v1) (->col-matrix v2))

where `v2' is the assumed-correct vector. If this value is less than 
1e-15, then (using the default error norm) the worst entry in `v1' has 
at least 15 correct digits (sort of). (If less than 1e-14, the worst 
entry has at least 14 correct digits, and so on.)

But you have a *set* of vectors. A straightforward way to test this is 
this way:

(require math/matrix)

(define (setof-vector=? vs1 vs2 [eps 1e-14])
   (let ([vs1  (map ->col-matrix (set->list vs1))]
         [vs2  (map ->col-matrix (set->list vs2))])
     (for/or ([vs1  (in-list (permutations vs1))])
       (for/and ([v1  (in-list vs1)]
                 [v2  (in-list vs2)])
         (< (matrix-relative-error v1 v2) eps)))))

where `v2' is assumed to be the correct set of vectors. So we have

 > (setof-vector=? (set #(4 5 6.00000000000001)
                        #(1 2 3.000000000000004))
                   (set #(1 2 3)
                        #(4 5 6)))

 > (setof-vector=? (set #(4 5 6.00000000000001)
                        #(1 2 3.00000000000004))
                   (set #(1 2 3)
                        #(4 5 6)))

But we have to ask whether there is a *permutation* of `vs1' elements 
which, when matched up with those in `vs2', all have relative error less 
than `eps'. That's O(n!) in the number of elements.

There's probably a tricky way to do this efficiently, but I don't know 
what it is. You might find one in your problem domain; e.g. there may be 
a fast way to test whether a basis is approximately the same as another 
basis, particularly if they're each orthogonal.

If you could avoid approximate equality testing on sets, that would be 
even better. Without context, it's ambiguous. Even with context, it's 
hard to figure out. For example, what should the following test return?

 > (setof-vector=? (set #(1.00000000000001 1.00000000000001)
                        #(0.99999999999999 0.99999999999999))
                   (set #(1 1)))

Neil ⊥

On 12/21/2013 04:34 PM, J G Cho wrote:
> That looks very useful.
> On Sat, Dec 21, 2013 at 6:23 PM, Greg Hendershott
> <greghendershott at gmail.com <mailto:greghendershott at gmail.com>> wrote:
>      > check-= wants single value but unfortunately my values are
>     wrapped in set of vectors.
>     Oh I didn't notice that.
>     In that case you could define your own check -- that looks inside the
>     sets and vectors to do the approximate equality test -- using the
>     second variant of `define-binary-check` as described here:
>     http://docs.racket-lang.org/rackunit/api.html#%28form._%28%28lib._rackunit%2Fmain..rkt%29._define-binary-check%29%29
> ____________________
>    Racket Users list:
>    http://lists.racket-lang.org/users

Posted on the users mailing list.