Skip to content

Commit

Permalink
fix: make printer for query generation more efficient
Browse files Browse the repository at this point in the history
We should avoid building the resulting query string with `format`
repeatedly, because `format` creates an entirely new string in
each invocation. When the query is big, the cost is
prohibitively expensive.

This commit instead builds the query string by writing to a
string port directly (similar to the StringBuilder idiom in languages
like Java). As the fix needs to be done on Z3, cvc4, and cvc5, I also
take an opportunity to refactor them to reduce code redundancy.
  • Loading branch information
sorawee committed Aug 30, 2023
1 parent d9ae5ed commit 37605c0
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 181 deletions.
4 changes: 1 addition & 3 deletions picus/algorithms/dpvl.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,7 @@
(r1cs:rassert (r1cs:rneq (r1cs:rvar (vector-ref :varvec sid)) (r1cs:rvar (vector-ref :alt-varvec sid))))
(r1cs:rsolve))))))
; perform optimization
(define final-str (string-join (:interpret-r1cs
(r1cs:rcmds-append :opts final-cmds))
"\n"))
(define final-str (:interpret-r1cs (r1cs:rcmds-append :opts final-cmds)))
(define res (:solve final-str :arg-timeout #:output-smt? #f))
(define solved? (cond
[(equal? 'unsat (car res))
Expand Down
69 changes: 69 additions & 0 deletions picus/r1cs/common.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#lang racket/base

(provide interp
format-op
emit)

(require racket/match
syntax/parse/define
(prefix-in tokamak: "../tokamak.rkt")
(prefix-in r1cs: "./r1cs-grammar.rkt")
(for-syntax racket/base))

(define (format-op op proc args)
(match args
['() (tokamak:exit "empty operation: ~a" op)]
[(list x) (proc x)]
[(cons x xs)
(display "(")
(display op)
(display " ")
(proc x)
(display " ")
(format-op op proc xs)
(display ")")]))

(begin-for-syntax
(define-syntax-class item
(pattern x:string #:with gen #'(display x))
(pattern x #:with gen #'x)))

;; (emit X ...) prints in order, where an element could be either
;; a string literal or a function call to do further printing.
(define-syntax-parse-rule (emit xs:item ...)
(begin xs.gen ...))

(define (interp arg-r1cs prnt)
(define p (open-output-string))
(parameterize ([current-output-port p])
(let loop ([arg-r1cs arg-r1cs])
(prnt
arg-r1cs
(λ (arg-r1cs)
(match arg-r1cs

; command level
[(r1cs:rcmds vs)
(for ([v vs])
(loop v)
(newline))]

[(r1cs:rraw v) (display v)]
[(r1cs:rlogic v) (printf "(set-logic ~a)" v)]
[(r1cs:rdef v t) (emit "(declare-const " (loop v) " " (loop t) ")")]
[(r1cs:rassert v) (emit "(assert " (loop v) ")")]
[(r1cs:rcmt v) (printf "; ~a" v)]
[(r1cs:rsolve ) (display "(check-sat)\n(get-model)")]

; sub-command level
[(r1cs:req lhs rhs) (emit "(= " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rneq lhs rhs) (emit "(not (= " (loop lhs) " " (loop rhs) "))")]

[(r1cs:rand vs) (format-op "and" loop vs)]
[(r1cs:ror vs) (format-op "or" loop vs)]
[(r1cs:rimp lhs rhs) (emit "(=> " (loop lhs) " " (loop rhs) ")")]

[(r1cs:rvar v) (display v)]
[(r1cs:rtype v) (display v)]
[_ (tokamak:exit "not supported: ~a" arg-r1cs)])))))
(get-output-string p))
84 changes: 23 additions & 61 deletions picus/r1cs/r1cs-cvc4-interpreter.rkt
Original file line number Diff line number Diff line change
@@ -1,65 +1,27 @@
#lang racket
; this interprets r1cs commands into z3 constraints
(require
(prefix-in tokamak: "../tokamak.rkt")
(prefix-in utils: "../utils.rkt")
(prefix-in config: "../config.rkt")
(prefix-in r1cs: "./r1cs-grammar.rkt")
)
(provide (rename-out
[interpret-r1cs interpret-r1cs]
))
; this interprets r1cs commands into cvc4 constraints
(require (prefix-in r1cs: "./r1cs-grammar.rkt")
"common.rkt")

(define (make-format-op op)
(define (format-op x y)
(cond
; [(and (null? x) (null? y)) (tokamak:exit "x and y can't both be null")]
[(and (null? x) (null? y)) ""]
[(null? x) y]
[(null? y) x]
[else (format "(~a ~a ~a)" op x y)]
)
)
; return a function
format-op
)
(provide interpret-r1cs)

(define (interpret-r1cs arg-r1cs)
(match arg-r1cs

; command level
[(r1cs:rcmds vs) (for/list ([v vs]) (interpret-r1cs v))]

[(r1cs:rraw v) (format "~a" v)]
[(r1cs:rlogic v) (format "(set-logic ~a)" v)]
[(r1cs:rdef v t) (format "(declare-const ~a ~a)" (interpret-r1cs v) (interpret-r1cs t))]
[(r1cs:rassert v) (format "(assert ~a)" (interpret-r1cs v))]
[(r1cs:rcmt v) (format "; ~a" v)]
[(r1cs:rsolve ) "(check-sat)\n(get-model)"]

; sub-command level
[(r1cs:req lhs rhs) (format "(= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rneq lhs rhs) (format "(not (= ~a ~a))" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rleq lhs rhs) (format "(<= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rlt lhs rhs) (format "(< ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rgeq lhs rhs) (format "(>= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rgt lhs rhs) (format "(> ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]

[(r1cs:rand vs) (foldr (make-format-op "and") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:ror vs) (foldr (make-format-op "or") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rimp lhs rhs) (format "(=> ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]

[(r1cs:rint v) (format "~a" v)]
[(r1cs:rvar v) (format "~a" v)]
[(r1cs:rtype v) (format "~a" v)]

[(r1cs:radd vs) (foldr (make-format-op "+") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rsub vs) (foldr (make-format-op "-") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rmul vs) (foldr (make-format-op "*") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rneg v) (format "(- ~a)" (interpret-r1cs v))]
; use mod for cvc4 since there's no rem
[(r1cs:rmod v mod) (format "(mod ~a ~a)" (interpret-r1cs v) (interpret-r1cs mod))]

[else (tokamak:exit "not supported: ~a" arg-r1cs)]
)
)
(interp
arg-r1cs
(λ (arg-r1cs fallback)
(let loop ([arg-r1cs arg-r1cs])
(match arg-r1cs
[(r1cs:rleq lhs rhs) (emit "(<= " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rlt lhs rhs) (emit "(< " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rgeq lhs rhs) (emit "(>= " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rgt lhs rhs) (emit "(> " (loop lhs) " " (loop rhs) ")")]

[(r1cs:rint v) (display v)]

[(r1cs:radd vs) (format-op "+" loop vs)]
[(r1cs:rsub vs) (format-op "-" loop vs)]
[(r1cs:rmul vs) (format-op "*" loop vs)]
[(r1cs:rneg v) (emit "(- " (loop v) ")")]
; use mod for cvc4 since there's no rem
[(r1cs:rmod v mod) (emit "(mod " (loop v) " " (loop mod) ")")]
[_ (fallback arg-r1cs)])))))
69 changes: 14 additions & 55 deletions picus/r1cs/r1cs-cvc5-interpreter.rkt
Original file line number Diff line number Diff line change
@@ -1,58 +1,17 @@
#lang racket
; this interprets r1cs commands into z3 constraints
(require
(prefix-in tokamak: "../tokamak.rkt")
(prefix-in utils: "../utils.rkt")
(prefix-in config: "../config.rkt")
(prefix-in r1cs: "./r1cs-grammar.rkt")
)
(provide (rename-out
[interpret-r1cs interpret-r1cs]
))

(define (make-format-op op)
(define (format-op x y)
(cond
; [(and (null? x) (null? y)) (tokamak:exit "x and y can't both be null")]
[(and (null? x) (null? y)) ""]
[(null? x) y]
[(null? y) x]
[else (format "(~a ~a ~a)" op x y)]
)
)
; return a function
format-op
)
; this interprets r1cs commands into cvc5 constraints
(require (prefix-in config: "../config.rkt")
(prefix-in r1cs: "./r1cs-grammar.rkt")
"common.rkt")
(provide interpret-r1cs)

(define (interpret-r1cs arg-r1cs)
(match arg-r1cs

; command level
[(r1cs:rcmds vs) (for/list ([v vs]) (interpret-r1cs v))]

[(r1cs:rraw v) (format "~a" v)]
[(r1cs:rlogic v) (format "(set-logic ~a)" v)]
[(r1cs:rdef v t) (format "(declare-const ~a ~a)" (interpret-r1cs v) (interpret-r1cs t))]
[(r1cs:rassert v) (format "(assert ~a)" (interpret-r1cs v))]
[(r1cs:rcmt v) (format "; ~a" v)]
[(r1cs:rsolve ) "(check-sat)\n(get-model)"]

; sub-command level
[(r1cs:req lhs rhs) (format "(= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rneq lhs rhs) (format "(not (= ~a ~a))" (interpret-r1cs lhs) (interpret-r1cs rhs))]

[(r1cs:rand vs) (foldr (make-format-op "and") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:ror vs) (foldr (make-format-op "or") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rimp lhs rhs) (format "(=> ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]

[(r1cs:rint v) (format "#f~am~a" v config:p)]
[(r1cs:rvar v) (format "~a" v)]
[(r1cs:rtype v) (format "~a" v)]

[(r1cs:radd vs) (foldr (make-format-op "ff.add") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rmul vs) (foldr (make-format-op "ff.mul") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rmod v mod) (tokamak:exit "cvc5 doesn't support mod")]

[else (tokamak:exit "not supported: ~a" arg-r1cs)]
)
)
(interp
arg-r1cs
(λ (arg-r1cs fallback)
(let loop ([arg-r1cs arg-r1cs])
(match arg-r1cs
[(r1cs:rint v) (printf "#f~am~a" v config:p)]
[(r1cs:radd vs) (format-op "ff.add" loop vs)]
[(r1cs:rmul vs) (format-op "ff.mul" loop vs)]
[_ (fallback arg-r1cs)])))))
80 changes: 21 additions & 59 deletions picus/r1cs/r1cs-z3-interpreter.rkt
Original file line number Diff line number Diff line change
@@ -1,64 +1,26 @@
#lang racket
; this interprets r1cs commands into z3 constraints
(require
(prefix-in tokamak: "../tokamak.rkt")
(prefix-in utils: "../utils.rkt")
(prefix-in config: "../config.rkt")
(prefix-in r1cs: "./r1cs-grammar.rkt")
)
(provide (rename-out
[interpret-r1cs interpret-r1cs]
))
(require (prefix-in r1cs: "./r1cs-grammar.rkt")
"common.rkt")

(define (make-format-op op)
(define (format-op x y)
(cond
; [(and (null? x) (null? y)) (tokamak:exit "x and y can't both be null")]
[(and (null? x) (null? y)) ""]
[(null? x) y]
[(null? y) x]
[else (format "(~a ~a ~a)" op x y)]
)
)
; return a function
format-op
)
(provide interpret-r1cs)

(define (interpret-r1cs arg-r1cs)
(match arg-r1cs

; command level
[(r1cs:rcmds vs) (for/list ([v vs]) (interpret-r1cs v))]

[(r1cs:rraw v) (format "~a" v)]
[(r1cs:rlogic v) (format "(set-logic ~a)" v)]
[(r1cs:rdef v t) (format "(declare-const ~a ~a)" (interpret-r1cs v) (interpret-r1cs t))]
[(r1cs:rassert v) (format "(assert ~a)" (interpret-r1cs v))]
[(r1cs:rcmt v) (format "; ~a" v)]
[(r1cs:rsolve ) "(check-sat)\n(get-model)"]

; sub-command level
[(r1cs:req lhs rhs) (format "(= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rneq lhs rhs) (format "(not (= ~a ~a))" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rleq lhs rhs) (format "(<= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rlt lhs rhs) (format "(< ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rgeq lhs rhs) (format "(>= ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]
[(r1cs:rgt lhs rhs) (format "(> ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]

[(r1cs:rand vs) (foldr (make-format-op "and") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:ror vs) (foldr (make-format-op "or") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rimp lhs rhs) (format "(=> ~a ~a)" (interpret-r1cs lhs) (interpret-r1cs rhs))]

[(r1cs:rint v) (format "~a" v)]
[(r1cs:rvar v) (format "~a" v)]
[(r1cs:rtype v) (format "~a" v)]

[(r1cs:radd vs) (foldr (make-format-op "+") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rsub vs) (foldr (make-format-op "-") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rmul vs) (foldr (make-format-op "*") null (for/list ([v vs]) (interpret-r1cs v)))]
[(r1cs:rneg v) (format "(- ~a)" (interpret-r1cs v))]
[(r1cs:rmod v mod) (format "(rem ~a ~a)" (interpret-r1cs v) (interpret-r1cs mod))]

[else (tokamak:exit "not supported: ~a" arg-r1cs)]
)
)
(interp
arg-r1cs
(λ (arg-r1cs fallback)
(let loop ([arg-r1cs arg-r1cs])
(match arg-r1cs
[(r1cs:rleq lhs rhs) (emit "(<= " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rlt lhs rhs) (emit "(< " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rgeq lhs rhs) (emit "(>= " (loop lhs) " " (loop rhs) ")")]
[(r1cs:rgt lhs rhs) (emit "(> " (loop lhs) " " (loop rhs) ")")]

[(r1cs:rint v) (display v)]

[(r1cs:radd vs) (format-op "+" loop vs)]
[(r1cs:rsub vs) (format-op "-" loop vs)]
[(r1cs:rmul vs) (format-op "*" loop vs)]
[(r1cs:rneg v) (emit "(- " (loop v) ")")]
[(r1cs:rmod v mod) (emit "(rem " (loop v) " " (loop mod) ")")]
[_ (fallback arg-r1cs)])))))
6 changes: 3 additions & 3 deletions picus/tokamak.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

; used for forced break out of the execution
(define (println-and-exit msg . fmts)
(printf (format "[tokamak:exit] ~a\n" (apply format (cons msg fmts))))
(eprintf (format "[tokamak:exit] ~a\n" (apply format (cons msg fmts))))
(exit 0)
)

Expand All @@ -32,7 +32,7 @@

(define (println-and-log msg . fmts)
;; TODO/fixme
(printf (format "[tokamak:log] ~a\n" (apply format (cons msg fmts))))
(eprintf (format "[tokamak:log] ~a\n" (apply format (cons msg fmts))))
)

; usually for debugging, asserting obj is one of types, otherwise print and exit
Expand Down Expand Up @@ -110,4 +110,4 @@
; not symbolic, so not decomposable
#f
)
)
)

0 comments on commit 37605c0

Please sign in to comment.