typed-racket/typed-racket-test/tr-random-testing.rkt
2015-11-10 16:39:18 -06:00

271 lines
7.2 KiB
Racket

#lang racket
;; Random testing of the TR numeric base environment, and numeric optimizations
(require redex/reduction-semantics
racket/flonum racket/unsafe/ops
racket/sandbox racket/cmdline
"random-real.rkt")
(require typed-racket/utils/utils
(typecheck typechecker)
(utils tc-utils)
(types subtype utils))
(require (prefix-in b: (base-env base-env))
(prefix-in n: (base-env base-env-numeric)))
(provide check-all-reals)
(b:init) (n:init)
(define-namespace-anchor anch)
(define-language tr-arith
[n real]
;; randomly generate F, not E, because literal numbers self-evaluate
;; (i.e. generates a useless test)
[E* n E F S I]
;; racket/math
;; [E (degrees->radians E)
;; (radians->degrees E)
;; (exact-round E)
;; (exact-floor E)
;; (exact-ceiling E)
;; (exact-truncate E)
;; (nan? E)
;; (infinite? E)
;; ]
;; more likely to be floats
[F* (real->double-flonum n) F] ; TODO fix pre-processing to avoid cast
[F (* F* ...)
(+ F* ...)
(- F* ...)
(/ F* ...)
(max F* ...)
(min F* ...)
(add1 F*)
(sub1 F*)
(abs F*)
(floor F*)
(ceiling F*)
(truncate F*)
(round F*)
(cos F*)
(sin F*)
(tan F*)
(sqr F*)
(flabs F*)
(flround F*)
(flfloor F*)
(flceiling F*)
(fltruncate F*)
(flsin F*)
(flcos F*)
(fltan F*)
(flatan F*)
(flasin F*)
(flacos F*)
(fllog F*)
(flexp F*)
(flsqrt F*)
(unsafe-flabs F*)
(unsafe-flmin F* F*)
(unsafe-flmax F* F*)
(unsafe-flsqrt F*)
(fl+ F* F*)
(fl- F* F*)
(fl* F* F*)
(fl/ F* F*)
(flmin F* F*)
(flmax F* F*)
(flexpt F* F*)
(unsafe-fl+ F* F*)
(unsafe-fl- F* F*)
(unsafe-fl* F* F*)
(unsafe-fl/ F* F*)]
;; not many single-flonum-specific ops, so will mostly be used in E context
[S (real->single-flonum n)
(inexact->exact S)
(real->double-flonum S)]
;; more likely to be integers
[I* (exact-round n) I] ; TODO fix pre-processing to avoid cast
[I (* I* ...)
(+ I* ...)
(- I* ...)
(max I* ...)
(min I* ...)
(add1 I*)
(sub1 I*)
(abs I*)
(floor I*)
(ceiling I*)
(truncate I*)
(round I*)
(sqr I*)
(modulo I* I*)
(remainder I* I*)
(quotient I* I*)
(gcd I*)
(gcd I* I*)
(gcd I* I* I*)
(lcm I*)
(lcm I* I*)
(lcm I* I* I*)
(arithmetic-shift I* I*)
(bitwise-and I* ...)
(bitwise-ior I* ...)
(bitwise-xor I* ...)
(bitwise-not I*)
(integer-length I*)
]
[E (* E* ...)
(+ E* ...)
(- E* ...)
(/ E* ...)
(max E* ...)
(min E* ...)
(add1 E*)
(sub1 E*)
(abs E*)
(floor E*)
(ceiling E*)
(truncate E*)
(round E*)
(sqrt E*)
(log E*)
(exp E*)
(cos E*)
(sin E*)
(tan E*)
(sqr E*)
(make-rectangular E* E*)
(make-polar E* E*)
(sinh E*)
(cosh E*)
(tanh E*)
(expt E* E*)
])
;; generated from: (map car (file->list "base-env-parts"))
;; Redex can't generate reals, so we convert ints to reals.
(define (exp->real-exp E) ; numbers or symbols or lists
(cond [(number? E)
(random-integer->random-real
(exact-round
;; doesn't handle non-rationals
;; not a problem, we generate those specially
(if (rational? E)
E
0)))] ; arbitrary
[(list? E)
(map exp->real-exp E)]
[else
E]))
;; big-step preservation: is the type of the result consistent with the type
;; predicted by the typechecker
(define (get-type e [typecheck (compose tc-expr expand)])
(parameterize ([delay-errors? #f]
[current-namespace (namespace-anchor->namespace anch)]
[orig-module-stx (quote-syntax e)])
(typecheck (datum->syntax #'here e))))
(define (right-type? before)
(define type-before (match (get-type before) [(tc-result1: b) b]))
(define after (with-handlers ([values values])
(eval before (namespace-anchor->namespace anch))))
(cond [(exn? after) #t]
[else
(define type-after (get-type after tc-literal))
(define subtype? (subtype type-after type-before))
(unless subtype?
(printf "type-before = ~v~ntype-after = ~v~n" type-before type-after))
subtype?]))
;; do we get the same result with and without optimization?
(define max-mem 1000)
(define (mk-eval lang)
(call-with-trusted-sandbox-configuration
(λ ()
(parameterize ([sandbox-memory-limit max-mem])
(make-evaluator lang #:requires '(racket/flonum racket/unsafe/ops))))))
(define racket-eval (mk-eval 'racket))
(define tr-eval (mk-eval 'typed/racket))
(define (same-result-as-untyped? sexp)
(define racket-failed? #f)
(define both-failed? #f)
(define racket-result
(with-handlers
;; something went wrong, e.g. division by zero
;; TR must fail too
([exn? (λ (e) (set! racket-failed? #t))])
(racket-eval sexp)))
(define tr-result
(with-handlers
;; did Racket error too?
([exn? (λ (e) (when racket-failed?
(set! both-failed? #t)))])
(tr-eval sexp)))
(or both-failed?
(and (not racket-failed?)
;; for NaN, which is not = to itself
(equal? racket-result tr-result))))
(define num-exceptions 0)
(define (check-all-reals sexp [verbose? #f])
;; because some of the generated expressions comute gigantic bignums, running
;; out of resources is expected, so just ignore that case
(with-handlers ([exn:fail:resource? values])
(with-limits
5 max-mem
(when verbose? (displayln sexp))
(or (with-handlers ; attempt to typecheck
;; something went wrong, almost certainly typechecking failed
;; in which case we ignore the expression
([exn? (λ (e)
(set! num-exceptions (+ num-exceptions 1))
#t)])
(get-type sexp)
#f) ; go on and check properties
(and (right-type? sexp)
(same-result-as-untyped? sexp))))))
(module+ main
(define n-attempts 1000)
(define seed (+ 1 (random (expt 2 30))))
(define verbose? #f)
(command-line
#:once-each
[("-n") n "Number of attempts" (set! n-attempts (string->number n))]
[("-s") s "RNG seed" (set! seed (string->number s))]
[("-v") "Print test cases" (set! verbose? #t)])
(random-seed seed)
(printf "seed: ~s~n" seed)
(flush-output) ; DrDr doesn't print the above if the testing segfaults.
;; start with 1000 small, deterministic test cases, to catch regressions
(redex-check tr-arith E #:in-order (check-all-reals (term E) verbose?)
#:attempts 1000
#:prepare exp->real-exp
#:keep-going? #t)
;; then switch to purely random to get different ones every run
(redex-check tr-arith E #:ad-hoc (check-all-reals (term E) verbose?)
#:attempts n-attempts
#:prepare exp->real-exp
#:keep-going? #t)
(printf "bad tests (usually typechecking failed): ~v~n" num-exceptions))
(module+ test
(module config info
(define timeout 600)
(define random? #t)))