Compare commits

...

1 Commits

Author SHA1 Message Date
Emina Torlak d333cb9ef9 Added objectives extraction that works only for optimizing constants (not expressions). 2016-03-14 18:17:29 -07:00
4 changed files with 185 additions and 50 deletions

View File

@ -4,7 +4,7 @@
check-sat get-model get-unsat-core check-sat get-model get-unsat-core
read-solution true false) read-solution true false)
"env.rkt" "enc.rkt" "env.rkt" "enc.rkt"
(only-in "../../base/core/term.rkt" constant? term-type solvable-default) (only-in "../../base/core/term.rkt" constant? term-type solvable-default type-of)
(only-in "../../base/core/function.rkt" fv function? function-domain function-range) (only-in "../../base/core/function.rkt" fv function? function-domain function-range)
(only-in "../../base/core/bool.rkt" @boolean?) (only-in "../../base/core/bool.rkt" @boolean?)
(only-in "../../base/core/bitvector.rkt" bitvector? bv) (only-in "../../base/core/bitvector.rkt" bitvector? bv)
@ -62,7 +62,7 @@
; declared or defined in P is bound in (decls env) or (defs env), respectively. ; declared or defined in P is bound in (decls env) or (defs env), respectively.
(define (decode env) (define (decode env)
(match (read-solution) (match (read-solution)
[(? hash? sol) [(cons (? hash? sol) (? hash? objs))
(sat (for/hash ([(decl id) (in-dict (decls env))]) (sat (for/hash ([(decl id) (in-dict (decls env))])
(let ([t (term-type decl)]) (let ([t (term-type decl)])
(values decl (values decl
@ -70,7 +70,13 @@
(if (function? t) (if (function? t)
(decode-function t (hash-ref sol id)) (decode-function t (hash-ref sol id))
(decode-value t (hash-ref sol id))) (decode-value t (hash-ref sol id)))
(solvable-default t))))))] (solvable-default t)))))
(if (hash-empty? objs)
(hash)
(for/hash ([(obj id) (in-sequences (in-dict (decls env)) (in-dict (defs env)))]
#:when (hash-has-key? objs id))
(values obj
(decode-objective (type-of obj) (hash-ref objs id))))))]
[(? list? names) [(? list? names)
(unsat (let ([core (apply set (map name->id names))]) (unsat (let ([core (apply set (map name->id names))])
(for/list ([(bool id) (in-sequences (in-dict (decls env)) (in-dict (defs env)))] (for/list ([(bool id) (in-sequences (in-dict (decls env)) (in-dict (defs env)))]
@ -78,6 +84,7 @@
bool)))] bool)))]
[#f (unsat)])) [#f (unsat)]))
(define (to-exact-int a) (if (integer? a) (inexact->exact a) a)) (define (to-exact-int a) (if (integer? a) (inexact->exact a) a))
(define (decode-value type val) (define (decode-value type val)
@ -132,7 +139,51 @@
(or (and (equal? p x) y) (or (and (equal? p x) y)
(and (equal? p y) x))])))) (and (equal? p y) x))]))))
(define (decode-objective type expr)
(match expr
[(app decode-objective-value (cons val ε))
(if (bitvector? type)
(interval (bv val type) #t (bv val type) #t)
(cond [(zero? ε) (interval val #t val #t)]
[(positive? ε) (interval val #f +inf.0 #f)]
[else (interval -inf.0 #f val #f)]))]
[(list (app decode-objective-value (cons min-val minε))
(app decode-objective-value (cons max-val maxε)))
(if (bitvector? type)
(interval (bv min-val type) #t (bv max-val type) #t)
(interval min-val (zero? minε) max-val (zero? maxε)))]))
(define (decode-objective-value expr)
(match (normalize-objective-value expr)
[(? real? r) (cons r 0)]
[(== 'epsilon) (cons 0 1)]
[(list (== '*) (? real? r) (== 'epsilon)) (cons 0 r)]
[(list (== '+) (? real? r) (== 'epsilon)) (cons r 1)]
[(list (== '+) (? real? r) (list (== '*) (? real? s) (== 'epsilon))) (cons r s)]
[_ #f]))
(define (normalize-objective-value expr)
(match expr
[(or (? real?) (== 'epsilon)) expr]
[(== 'oo) +inf.0]
[(list (== 'to_real) r) r]
[(list (== '-) r) (- r)]
[(list (== '+) e0 e1)
(match* ((normalize-objective-value e0) (normalize-objective-value e1))
[((? real? r0) (? real? r1)) (+ r0 r1)]
[((? real? r) other) `(+ ,r ,other)]
[(other (? real? r)) `(+ ,r ,other)])]
[(list (== '*) e0 e1)
(match* ((normalize-objective-value e0) (normalize-objective-value e1))
[((? real? r0) (? real? r1)) (* r0 r1)]
[((? real? r) other) `(* ,r ,other)]
[(other (? real? r)) `(* ,r ,other)])]
[_ #f]))

View File

@ -7,26 +7,34 @@
; Reads the SMT solution from current-input-port. ; Reads the SMT solution from current-input-port.
; The solution consist of 'sat or 'unsat, followed by ; The solution consist of 'sat or 'unsat, followed by
; followed by a suitably formatted s-expression. The ; followed by a suitably formatted s-expression.
; output of this procedure is a hashtable from constant ; If the solution is 'sat, the output of this procedure is a pair of
; identifiers to their values (if the solution is 'sat); ; hashtables, where the first table represents the model as a
; a non-empty list of assertion identifiers that form an ; map from constant identifiers to their values, and the second table
; unsatisfiable core (if the solution is 'unsat and a ; maps objective terms to values. If the solution is 'unsat
; core was extracted); or #f (if the solution is ; and a core was extracted, the procedure outputs a non-empty
; 'unsat and no core was extracted). ; list of assertion identifiers that form an
; unsatisfiable core. If the solution is 'unsat and no core was
; extracted, the output is #f.
(define (read-solution) (define (read-solution)
(define port (current-input-port)) (define port (current-input-port))
(match (read port) (match (read port)
[(== 'sat) [(== 'sat)
(define model (make-hash))
(define objectives (make-hash))
(let loop () (let loop ()
(match (read port) (match (read port)
[(list (== 'objectives) _ ...) (loop)] [(list (== 'objectives) (list term val) ...)
(for ([t term] [v val])
(hash-set! objectives t v))
(loop)]
[(list (== 'model) def ...) [(list (== 'model) def ...)
(for/hash ([d def]) (for ([d def])
(match d (match d
[(list (== 'define-fun) c '() _ v) (values c v)] [(list (== 'define-fun) c '() _ v) (hash-set! model c v)]
[(list (== 'define-fun) c _ ...) (values c d)]))] [(list (== 'define-fun) c _ ...) (hash-set! model c d)]))]
[other (error 'solution "expected model, given ~a" other)]))] [other (error 'solution "expected model, given ~a" other)]))
(cons model objectives)]
[(== 'unsat) [(== 'unsat)
(match (read port) (match (read port)
[(list (? symbol? name) ...) name] [(list (? symbol? name) ...) name]

View File

@ -1,13 +1,18 @@
#lang racket #lang racket
(require "../base/core/term.rkt" (require "../base/core/term.rkt"
(only-in "../base/core/term.rkt" type-of)) (only-in "../base/core/term.rkt" type-of)
(only-in "../base/core/bitvector.rkt" bv? @bvsle))
(provide solution? sat? unsat? sat unsat model core) (provide solution? sat? unsat? sat unsat model core objectives
interval? interval interval-min interval-max
interval-includes-min? interval-includes-max?)
; Represents the solution to a set of logical constraints. The solution ; Represents the solution to a set of logical constraints. The solution
; has single field, result, which stores either a model of the constraints, ; has two fields: result and objectives. The result field stores either
; an unsatisfiable core, or #f. ; a model of the constraints, an unsatisfiable core, or #f. The objectives
; field maps optimization objective terms, if any, to intervals indicating the best
; values those objectives may take in a given solution.
; ;
; If the constraints are satisfiable, the result is dictionary binding symbolic ; If the constraints are satisfiable, the result is dictionary binding symbolic
; constants to values. In that case, the solution ; constants to values. In that case, the solution
@ -15,20 +20,29 @@
; returns a concrete value for that constant, if any, or the constant itself, if the ; returns a concrete value for that constant, if any, or the constant itself, if the
; model has no binding for that constant. ; model has no binding for that constant.
; ;
; If the constraints are unsatisfiable, and no core has been extracted, the result field is #f. ; If the constraints are unsatisfiable, and no core has been extracted, the result
; If a core has been extracted, the result is a list of constraints (that is @boolean? terms or ; field is #f. If a core has been extracted, the result is a list of constraints
; values) that do not have a model. ; (that is @boolean? terms or values) that do not have a model. The objectives field
(struct solution (result) ; is #f in the case of unsatisfiability.
(struct solution (result objectives)
#:property prop:procedure #:property prop:procedure
(lambda (self arg) (lambda (self arg)
(match self (match self
[(solution (? dict? model)) [(solution (? dict? model) _)
(if (dict-has-key? model arg) (if (dict-has-key? model arg)
(dict-ref model arg) (dict-ref model arg)
arg)] arg)]
[else (error 'solution "cannot query an unsat solution: ~s" self)])) [else (error 'solution "cannot query an unsat solution: ~s" self)]))
#:methods gen:custom-write #:methods gen:custom-write
[(define (write-proc self port mode) [(define (write-proc self port mode)
(match (solution-objectives self)
[(? dict? objs)
(unless (dict-empty? objs)
(fprintf port "(objectives")
(for ([(k v) (in-dict objs)])
(fprintf port "\n [~a ~a]" k v))
(fprintf port ")\n"))]
[_ (void)])
(match (solution-result self) (match (solution-result self)
[(? dict? model) [(? dict? model)
(let ([bindings (sort (dict->list model) term<? #:key car)]) (let ([bindings (sort (dict->list model) term<? #:key car)])
@ -49,45 +63,54 @@
(define (unsat? sol) (not (sat? sol))) (define (unsat? sol) (not (sat? sol)))
(define (objectives sol) (solution-objectives sol))
(define (get-model s) (define (get-model s)
(match s (match s
[(solution (? dict? m)) m] [(solution (? dict? m) _) m]
[_ (error 'model "expected a sat? solution, given ~a" s)])) [_ (error 'model "expected a sat? solution, given ~a" s)]))
(define (get-core s) (define (get-core s)
(match s (match s
[(solution #f) #f] [(solution #f _) #f]
[(solution (? list? c)) c] [(solution (? list? c) _) c]
[_ (error 'model "expected an unsat? solution, given ~a" s)])) [_ (error 'model "expected an unsat? solution, given ~a" s)]))
(define-match-expander model (define-match-expander model
(syntax-rules () (syntax-rules ()
[(model) (solution (? dict? (app dict-count 0)))] [(model) (solution (? dict? (app dict-count 0)) _)]
[(model pat) (solution (? dict? pat))]) [(model pat) (solution (? dict? pat) _)])
(syntax-id-rules (set!) (syntax-id-rules (set!)
[(model s) (get-model s)] [(model s) (get-model s)]
[model get-model])) [model get-model]))
(define-match-expander core (define-match-expander core
(syntax-rules () (syntax-rules ()
[(core) (solution #f)] [(core) (solution #f #f)]
[(core #f) (solution #f)] [(core #f) (solution #f #f)]
[(core pat) (solution (? list? pat))]) [(core pat) (solution (? list? pat) #f)])
(syntax-id-rules (set!) (syntax-id-rules (set!)
[(core s) (get-core s)] [(core s) (get-core s)]
[core get-core])) [core get-core]))
(define empty-sat (solution (hash))) (define empty-sat (solution (hash) (hash)))
(define empty-unsat (solution #f)) (define empty-unsat (solution #f #f))
; Creates and returns a satisfiable solution consisting of the given model. The model ; Creates and returns a satisfiable solution consisting of the given model. The model
; must be an immutable dictionary, with symbolic constants as keys. ; must be an immutable dictionary, with symbolic constants as keys.
(define sat (define sat
(case-lambda (case-lambda
[() empty-sat] [() empty-sat]
[(model) (unless (and (dict? model) (not (dict-mutable? model))) [(model)
(error 'sat "expected an immutable dictionary, given ~s" model)) (unless (and (dict? model) (not (dict-mutable? model)))
(solution model)])) (raise-arguments-error 'sat "expected an immutable dictionary" "model" model))
(solution model (hash))]
[(model objs)
(unless (and (dict? model) (not (dict-mutable? model))
(dict? objs) (not (dict-mutable? objs)))
(raise-arguments-error 'sat "expected immutable dictionaries"
"model" model "objectives" objs))
(solution model objs)]))
; Creates and returns a new unsatisfiable solution that consists of the given core, if any, ; Creates and returns a new unsatisfiable solution that consists of the given core, if any,
; or no core, if called with no arguments. The must be a list of @boolean? values. ; or no core, if called with no arguments. The must be a list of @boolean? values.
@ -95,5 +118,25 @@
(case-lambda [() empty-unsat] (case-lambda [() empty-unsat]
[(core) (unless (and (list? core) (not (null? core))) [(core) (unless (and (list? core) (not (null? core)))
(error 'unsat "expected a non-empty list, given ~s" core)) (error 'unsat "expected a non-empty list, given ~s" core))
(solution core)])) (solution core #f)]))
(struct interval (min includes-min? max includes-max?)
#:transparent
#:guard (lambda (min includes-min? max includes-max? name)
(unless (boolean? includes-min?)
(raise-arguments-error name "expected a boolean?" "includes-min?" includes-min?))
(unless (boolean? includes-max?)
(raise-arguments-error name "expected a boolean?" "includes-max?" includes-max?))
(unless (or (and (real? min) (real? max) (<= min max))
(and (bv? min) (bv? max) (@bvsle min max)))
(raise-arguments-error name "expected two real? or bv? values with min <= max"
"min" min "max" max))
(values min includes-min? max includes-max?))
#:methods gen:custom-write
[(define (write-proc self port mode)
(match-define (interval min includes-min? max includes-max?) self)
(fprintf port (if includes-min? "[" "("))
(fprintf port "~a " min)
(fprintf port "~a" max)
(fprintf port (if includes-max? "]" ")")))])

View File

@ -3,34 +3,67 @@
(require rackunit rackunit/text-ui rosette/lib/roseunit) (require rackunit rackunit/text-ui rosette/lib/roseunit)
(define (minimize obj . asserts) (define (minimize obj . asserts)
(optimize #:minimize (list obj) (optimize #:minimize (if (list? obj) obj (list obj))
#:guarantee (for ([a asserts]) (assert a)))) #:guarantee (for ([a asserts]) (assert a))))
(define (maximize obj . asserts) (define (maximize obj . asserts)
(optimize #:maximize (list obj) (optimize #:maximize (if (list? obj) obj (list obj))
#:guarantee (for ([a asserts]) (assert a)))) #:guarantee (for ([a asserts]) (assert a))))
(define (check-solution actual expected [test =]) (define (check-solution actual test expected)
(check-sat actual) (check-sat actual)
;(printf "actual: ~a\nexpected: ~a\n" actual expected)
(define ma (model actual)) (define ma (model actual))
(define me (model expected)) (define me (model expected))
(check-equal? (dict-keys ma) (dict-keys me)) (check-equal? (dict-keys ma) (dict-keys me))
(for ([(k v) ma]) (for ([(k v) ma])
(check test v (dict-ref me k)))) (check test v (dict-ref me k)))
(define oa (objectives actual))
(define oe (objectives expected))
(check-equal? (dict-keys oa) (dict-keys oe))
(for ([(k v) oa])
(check-equal? v (dict-ref oe k))))
(define basic-tests (define basic-tests
(test-suite+ "Basic optimize tests with no finitization." (test-suite+ "Basic optimize tests with no finitization."
(current-bitwidth #f) (current-bitwidth #f)
(define-symbolic x y integer?) (define-symbolic x y z integer?)
(check-solution (maximize (+ x y) (< x 2) (< (- y x) 1))
(sat (hash x 1 y 1))) ; (check-solution
; (maximize (+ x y) (< x 2) (< (- y x) 1)) =
; (sat (hash x 1 y 1) (hash (+ x y) 2)))
(check-solution
(maximize z (< x 2) (< (- y x) 1) (= z (+ x y))) =
(sat (hash x 1 y 1 z 2) (hash z (interval 2 #t 2 #t))))
(check-unsat (maximize (+ x y) (< x 2) (< y 2) (> (+ x y) 4))) (check-unsat (maximize (+ x y) (< x 2) (< y 2) (> (+ x y) 4)))
(check-unsat (minimize (+ x y) (< x 2) (< y 2) (> (+ x y) 4))) (check-unsat (minimize (+ x y) (< x 2) (< y 2) (> (+ x y) 4)))
(check-solution (minimize (+ x y) (< x 4) (< (- y x) 1) (> y 1))
(sat (hash x 2 y 2))) ; (check-solution
(define-symbolic r q real?) ; (minimize (+ x y) (< x 4) (< (- y x) 1) (> y 1)) =
(check-solution (maximize (+ r q) (< r 4) (< q 5)) ; (sat (hash x 2 y 2) (hash (+ x y) 4)))
(sat (hash r 3.0 q 4.0)) >=)
; (check-solution
; (maximize (+ x y) (< x 2) (> (- y x) 1)) >=
; (sat (hash x 1 y 3) (hash (+ x y) (interval +inf.0 #t +inf.0 #t))))
; (check-solution
; (minimize (+ x y) (< x 4) (< (- y x) 1) (< y 1)) <=
; (sat (hash x -1 y -1) (hash (+ x y) (interval -inf.0 #t -inf.0 #t))))
(check-solution
(maximize (list x y) (< x z) (< y z) (< z 5) (! (= x y))) =
(sat (hash z 4 y 2 x 3) (hash x (interval 3 #t 3 #t) y (interval 2 #t 2 #t))))
; (define-symbolic r q real?)
;
; (check-solution
; (maximize (+ r q) (< r 4) (< q 5)) >=
; (sat (hash r 3.0 q 4.0) (hash (+ r q) (interval -inf.0 #f 9 #f))))
;
)) ))
(time (run-tests basic-tests)) (time (run-tests basic-tests))