On Mon, May 8, 2017 at 3:05 AM, Matthias Felleisen <matth...@ccs.neu.edu> wrote:
> > Measure with outer contract and remove the inner define/tight. If you > wrote this algorithm with a loop in Eiffel, instead of a tail-recursive > function, you wouldn’t check the invariant for every loop iteration either > > Fair comment. [In Eiffel I'd need to use a check condition in my loop. Below I explore the moral equivalent of raising an error in Racket, which seems to work well enough.] I've removed the offending post-condition and redone the timings. (Code at end) *No contracts* cpu time: 13 real time: 13 gc time: 0 *Outer contract only* cpu time: 48 real time: 48 gc time: 0 *Outer contract + inner exception check* cpu time: 63 real time: 73 gc time: 15 *Outer + tight inner contract* cpu time: 1251 real time: 1268 gc time: 27 The virtue of the inner contract is that it catches non-terminating cases during development. E.g. In the recursive call (S (* 0.5 (+ estimate (/ x estimate)))) if we replace / with * the "tight" contract catches it by imposing a bounds check. *Note*: I haven't reintroduced the more stringent requirement that the error reduces with every iteration (monotonic convergence). *Take-away* The timings suggest that applying the contract system is a particularly expensive way to achieve this level of safety. The outer contract + inner exception check is a much more affordable alternative. This example isn't enough to really explore the question of whether it makes sense to later turn off the inner checks, once confidence has been achieved. Personally, I'd say "it depends" on how slow we're going and whether it makes a difference and what cheaper checks are available. YMMV Dan #lang racket (define-syntax-rule (define/tight (fn args ...) ctc body ...) (begin (define (fn args ...) (fn/impl args ...)) (with-contract fn ([fn/impl ctc]) #:freevar fn ctc (define (fn/impl args ...) body ...)))) (define-syntax-rule (implies a b) (or (not a) b)) (define (default-eps) 1e-14) (define (sqrt-error x estimate) (-> (>/c 0) (>/c 0) (>=/c 0)) (abs (- 1 (/ (sqr estimate) x)))) (define/contract (real-sqrt x [eps (default-eps)]) (->i ([x (>=/c 0)]) ([eps (>/c 0)]) [result (>=/c 0)] #:post (x result) (implies (= x 0) (= result 0)) #:post (x eps result) (let ([EPS (if (unsupplied-arg? eps) (default-eps) eps)]) (implies (> x 0) (< (sqrt-error x result) EPS)))) (define/tight (S estimate) (->i ([estimate (and (>/c 0) (<=/c (max 1 x)))]) [result (>/c 0)]) (if (< (sqrt-error x estimate) eps) estimate (S (* 0.5 (+ estimate (/ x estimate)))))) (if (zero? x) 0 (S (* 0.5 (+ 1 x))))) (define/contract (real-sqrt-moderate x [eps (default-eps)]) (->i ([x (>=/c 0)]) ([eps (>/c 0)]) [result (>=/c 0)] #:post (x result) (implies (= x 0) (= result 0)) #:post (x eps result) (let ([EPS (if (unsupplied-arg? eps) (default-eps) eps)]) (implies (> x 0) (< (sqrt-error x result) EPS)))) (define (sqrt-error estimate) (abs (- 1 (/ (sqr estimate) x)))) (define (S estimate) (if (< (sqrt-error estimate) eps) estimate (S (* 0.5 (+ estimate (/ x estimate)))))) (if (zero? x) 0 (S (* 0.5 (+ 1 x))))) (define/contract (real-sqrt-modified x [eps (default-eps)]) (->i ([x (>=/c 0)]) ([eps (>/c 0)]) [result (>=/c 0)] #:post (x result) (implies (= x 0) (= result 0)) #:post (x eps result) (let ([EPS (if (unsupplied-arg? eps) (default-eps) eps)]) (implies (> x 0) (< (sqrt-error x result) EPS)))) (define (sqrt-error estimate) (abs (- 1 (/ (sqr estimate) x)))) (define (S estimate) (unless (<= 0 estimate (max 1 x)) (raise-arguments-error 'real-sqrt-modified "Broken bounds" "estimate" estimate "x" x)) (if (< (sqrt-error estimate) eps) estimate (S (* 0.5 (+ estimate (/ x estimate)))))) (if (zero? x) 0 (S (* 0.5 (+ 1 x))))) (define (real-sqrt-fast x [eps (default-eps)]) (define (sqrt-error estimate) (abs (- 1 (/ (sqr estimate) x)))) (define (S estimate) (if (< (sqrt-error estimate) eps) estimate (S (* 0.5 (+ estimate (/ x estimate)))))) (if (zero? x) 0 (S (* 0.5 (+ 1 x))))) (displayln "No contracts") (time (for ([i (in-range 5000)]) (real-sqrt-fast i))) (newline) (displayln "Outer contract only") (time (for ([i (in-range 5000)]) (real-sqrt-moderate i))) (newline) (displayln "Outer contract + inner exception check") (time (for ([i (in-range 5000)]) (real-sqrt-modified i))) (newline) (displayln "Outer contract + tight inner contract") (time (for ([i (in-range 5000)]) (real-sqrt i))) -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.