Example #1
0
def define_fun_boolean_input(lispnode_list):
    "replace all define-fun functions with Bool inputs with BitVec 1 inputs"
    for lispnode in lispnode_list:
        for f in lispnode.find("define-fun"):
            if len(f.parent.children) > 3:
                function_token = f.parent.children[1]
                for input_index in range(len(f.parent.children[2].children)):
                    input_description = f.parent.children[2].children[
                        input_index]
                    if len(input_description.children) > 1:
                        if input_description.children[1] == "Bool":
                            # replace all instances of this in the function definition
                            for input_use in f.parent.children[4].find(
                                    input_description.children[0]):
                                replacement = LispNode("(= #b1 " +
                                                       str(input_use) + ")")
                                input_use.children = replacement.children
                                input_use.token = ""
                                input_use.is_token = False
                            # replace all inputs elsewhere, and change type
                            input_description.children[1] = LispNode(
                                "(_ BitVec 1)")
                            input_description.children[
                                1].parent = input_description
                            replace_boolean_function_input(
                                function_token, input_index, lispnode_list)
Example #2
0
def prepend_headers(all_lines_list, lispnode_list):
    "prepend appropriate headers for QF_BV logic"
    lispnode_list.insert(0, LispNode("(set-option :produce-models true)"))
    all_lines_list.insert(0, lispnode_list[0])
    lispnode_list.insert(0, LispNode("(set-logic QF_BV)"))
    all_lines_list.insert(0, lispnode_list[0])
    lispnode_list.insert(0, LispNode("(set-info :smt-lib-version 2.0)"))
    all_lines_list.insert(0, lispnode_list[0])
Example #3
0
def bv_all_zero_1_in_ith_place(n, i):
    "creates a node that evaluates to a bitvec size n with a 1 only in place i"
    if i >= n:
        return LispNode("((_ repeat " + str(n) + ") #b0)")
    if i == 0:
        return LispNode("((_ zero_extend " + str(n - 1) + ")  #b1)")
    return LispNode(children=[
        LispNode("concat"),
        bv_all_zero_1_in_ith_place(n - i, 0),
        bv_all_zero_1_in_ith_place(i, i)
    ])
Example #4
0
def replace_boolean_function_input(function_token, input_index, lispnode_list):
  """ Should replace the input_index th input of the function with the given
      function token with an if-than-else expression that takes the old
      boolean value, and returns #b1 if true of #b0 if false"""
  for lispnode in lispnode_list:
    for f in lispnode.find(function_token):
      if (len(f.parent.children) > input_index) and (f.parent.children[0] == f):
        replacement = LispNode("(ite "+str(f.parent.children[input_index+1])+\
                               " #b1 #b0)")
        replacement.parent = f.parent
        f.parent.children[input_index+1] = replacement
Example #5
0
def replace_boolean_function_input(function_token, input_index, lispnode_list):
    """ Should replace the input_index th input of the function with the given
      function token with an if-than-else expression that takes the old
      boolean value, and returns #b1 if true of #b0 if false"""
    for lispnode in lispnode_list:
        for f in lispnode.find(function_token):
            if (len(f.parent.children) > input_index) and (f.parent.children[0]
                                                           == f):
                replacement = LispNode("(ite "+str(f.parent.children[input_index+1])+\
                                       " #b1 #b0)")
                replacement.parent = f.parent
                f.parent.children[input_index + 1] = replacement
Example #6
0
def define_sort(lispnode_list):
    """reads through the lispnode list, replacing the sorts defined (provided they
     have 0 sort parameters) with their definitions"""
    for l in lispnode_list:
        for p in l.find(LispNode("define-sort")):
            if p.parent.children[2] == LispNode("()"):
                target = LispNode(p.parent.children[1])
                replacement = LispNode(p.parent.children[3])
                # we're going to replace p's parent with the empty token
                p.parent.is_token = True
                p.parent.token = ""
                for node in lispnode_list:
                    node.replace(target, replacement)
Example #7
0
def declare_fun_boolean_input(lispnode_list):
  "replace all declare-fun functions with Bool inputs with BitVec 1 inputs"
  for lispnode in lispnode_list:
    for f in lispnode.find("declare-fun"):
      if len(f.parent.children) > 2:
        function_token = f.parent.children[1]
        for input_index in range(len(f.parent.children[2].children)):
          input_description = f.parent.children[2].children[input_index]
          if input_description == "Bool":
            replacement = LispNode("(_ BitVec 1)")
            replacement.parent = f.parent.children[2]
            f.parent.children[2].children[input_index] = replacement
            replace_boolean_function_input(function_token, input_index,
                                             lispnode_list)
Example #8
0
def forall_single(lispnode_list):
    "replaces all foralls with multiple items with nested singleton foralls"
    for lispnode in lispnode_list:
        for forall in lispnode.find("forall"):
            parent = forall.parent
            while len(parent.children[1].children) > 1:
                parent.children[2] = LispNode(
                    parent=parent,
                    children=[
                        LispNode("forall"),
                        LispNode(children=parent.children[1].children[1:]),
                        parent.children[2]
                    ])
                parent.children[1].children = parent.children[1].children[:1]
                parent = parent.children[2]
Example #9
0
def exists_replace(lispnode_list):
    " replaces (exists foo bar) with (not (forall foo (not bar))) "
    for lispnode in lispnode_list:
        lispnode_index = lispnode_list.index(lispnode)
        for exists in lispnode.find("exists"):
            exists.parent.children = [
                LispNode("not", parent=exists.parent),
                LispNode(parent=exists.parent,
                         children=[
                             LispNode("forall"), exists.parent.children[1],
                             LispNode(children=[
                                 LispNode("not"), exists.parent.children[2]
                             ])
                         ])
            ]
Example #10
0
def declare_fun_boolean_input(lispnode_list):
    "replace all declare-fun functions with Bool inputs with BitVec 1 inputs"
    for lispnode in lispnode_list:
        for f in lispnode.find("declare-fun"):
            if len(f.parent.children) > 2:
                function_token = f.parent.children[1]
                for input_index in range(len(f.parent.children[2].children)):
                    input_description = f.parent.children[2].children[
                        input_index]
                    if input_description == "Bool":
                        replacement = LispNode("(_ BitVec 1)")
                        replacement.parent = f.parent.children[2]
                        f.parent.children[2].children[
                            input_index] = replacement
                        replace_boolean_function_input(function_token,
                                                       input_index,
                                                       lispnode_list)
Example #11
0
def local_max_thresholds_iterative(all_lines_list, lispnode_list,model,solver):
  define_funs_from_model(model, lispnode_list)
  thresholds_lookup = LispNode("")
  for lispnode in lispnode_list:
    for node in lispnode.find("thresholds-bv-version-lookup-table"):
      if node.parent.children[0] == "define-fun":
        thresholds_lookup = node.parent.children[4]
  if thresholds_lookup.token.startswith("#x"):
    thresholds_lookup.token = "#b"+hex_to_binary(thresholds_lookup.token[2:])
  for bit in range(2, len(thresholds_lookup.token)):
    if thresholds_lookup.token[bit] == "1":
      print bit
      print thresholds_lookup.token
      thresholds_lookup.token = thresholds_lookup.token[     :bit]+"0"+\
                                thresholds_lookup.token[bit+1:   ]
      #write file, use solver to determine if this is valid
      f_name = ("/tmp/iteration-"+\
               str("-".join(str(datetime.datetime.now()).split()))+".smt2")
      f = open(f_name,"w")
      for x in map(lambda x:str(x).strip(),all_lines_list):
        if (len(x) > 0):
          if (x[0] == "("):
            x = LispNode(x).pretty_print()
        f.write(x)
        f.write("\n")
      f.flush() 
      f.close() 
      model_filename = "/tmp/iteration-model"+\
               str("-".join(str(datetime.datetime.now()).split()))+".smt2"
      os.system(solver+" "+f_name+" > "+ model_filename)
      m = open(model_filename,"r").read()
      m = m.strip()
      if m.startswith("sat"):
        return local_max_thresholds_iterative(all_lines_list, lispnode_list,
                                              LispNode(m[3:]), solver)
      elif m.startswith("unsat"):
        thresholds_lookup.token = thresholds_lookup.token[     :bit]+"1"+\
                                  thresholds_lookup.token[bit+1:   ]
      else:
        print "ITERATIVE SOLVER FAILED:\n"+m
        return thresholds_lookup.token
  return thresholds_lookup.token
Example #12
0
def define_fun_no_input(all_lines_list, lispnode_list):
    """Because apparently some solvers can't handle it, replaces no input
     define-fun with declare fun and assertion"""
    for lispnode in lispnode_list:
        for define in lispnode.find("define-fun"):
            if define.parent.children[2] == "()":
                define.token = "declare-fun"
                lispnode_index = lispnode_list.index(lispnode)
                all_lines_index = all_lines_list.index(lispnode)
                lispnode_list.insert(
                    lispnode_index + 1,
                    LispNode(children=[
                        LispNode("assert"),
                        LispNode(children=[
                            LispNode("="),
                            LispNode(define.parent.children[1]),
                            define.parent.children.pop(4)
                        ])
                    ]))
                all_lines_list.insert(all_lines_index + 1,
                                      lispnode_list[lispnode_index + 1])
Example #13
0
def functions_boolean_output_to_bv(all_lines_list, lispnode_list):
    """if any declared functions have Bool outputs, creates a new declared
     version with BitVec output, and defines the old function to read from the
     new one. In order to bias z3 towards functiosn returning "true" in cases
     where it doesn't matter, the bv version returns 0 when the original
     returns true"""
    for lispnode in lispnode_list:
        for declare_fun in lispnode.find("declare-fun"):
            if declare_fun.parent.children[3] == "Bool":
                lispnode_index = lispnode_list.index(lispnode)
                all_lines_index = all_lines_list.index(lispnode)
                lispnode_list.insert(
                    lispnode_index,
                    LispNode(children=[
                        LispNode("declare-fun"),
                        LispNode(
                            str(declare_fun.parent.children[1]) +
                            "-bv-version"),
                        LispNode(declare_fun.parent.children[2]),
                        LispNode("(_ BitVec 1)")
                    ]))
                all_lines_list.insert(all_lines_index,
                                      lispnode_list[lispnode_index])
                declare_fun.parent.children[0].token = "define-fun"
                for i in range(len(declare_fun.parent.children[2].children)):
                    declare_fun.parent.children[2].children[i] = LispNode(
                        parent=declare_fun.parent.children[2],
                        children=[
                            LispNode("generated-input-name-" + str(i)),
                            declare_fun.parent.children[2].children[i]
                        ])
                declare_fun.parent.children.append(
                    LispNode(
                        "(= #b0 (" + str(declare_fun.parent.children[1]) +
                        "-bv-version " + " ".join(
                            map(
                                lambda i: "generated-input-name-" + str(i),
                                range(
                                    len(declare_fun.parent.children[2].children
                                        )))) + "))"))
Example #14
0
def local_max_thresholds_iterative(all_lines_list, lispnode_list, model,
                                   solver):
    define_funs_from_model(model, lispnode_list)
    thresholds_lookup = LispNode("")
    for lispnode in lispnode_list:
        for node in lispnode.find("thresholds-bv-version-lookup-table"):
            if node.parent.children[0] == "define-fun":
                thresholds_lookup = node.parent.children[4]
    if thresholds_lookup.token.startswith("#x"):
        thresholds_lookup.token = "#b" + hex_to_binary(
            thresholds_lookup.token[2:])
    for bit in range(2, len(thresholds_lookup.token)):
        if thresholds_lookup.token[bit] == "1":
            print bit
            print thresholds_lookup.token
            thresholds_lookup.token = thresholds_lookup.token[     :bit]+"0"+\
                                      thresholds_lookup.token[bit+1:   ]
            #write file, use solver to determine if this is valid
            f_name = ("/tmp/iteration-"+\
                     str("-".join(str(datetime.datetime.now()).split()))+".smt2")
            f = open(f_name, "w")
            for x in map(lambda x: str(x).strip(), all_lines_list):
                if (len(x) > 0):
                    if (x[0] == "("):
                        x = LispNode(x).pretty_print()
                f.write(x)
                f.write("\n")
            f.flush()
            f.close()
            model_filename = "/tmp/iteration-model"+\
                     str("-".join(str(datetime.datetime.now()).split()))+".smt2"
            os.system(solver + " " + f_name + " > " + model_filename)
            m = open(model_filename, "r").read()
            m = m.strip()
            if m.startswith("sat"):
                return local_max_thresholds_iterative(all_lines_list,
                                                      lispnode_list,
                                                      LispNode(m[3:]), solver)
            elif m.startswith("unsat"):
                thresholds_lookup.token = thresholds_lookup.token[     :bit]+"1"+\
                                          thresholds_lookup.token[bit+1:   ]
            else:
                print "ITERATIVE SOLVER FAILED:\n" + m
                return thresholds_lookup.token
    return thresholds_lookup.token
Example #15
0
def main(args):
    help_key = False
    analysis = False
    if len(args) < 1:
        help_key = True
    elif "-h" in args or "-help" in args:
        help_key = True
    if help_key:
        print """
    Pysmt: your friend when converting z3 SMT2 extensions and such to QF_BV
    If you have define-sort or declare-datatypes with no arguments, this may
    help you.
    Likewise, if you have functions with only BitVec or Boolean (or declared
    or defined datatypes) as inputs, this may help you.
    If you meet the above requirements, this may be able to convert your smt2
    file into a QF_BV smt2 file. It will unroll all quantifiers, replace all
    declared datatypes and defined sorts with what you defined them to be,
    replace all boolean function inputs with single bit bitvectors, (and fix
    any usage appropriately), and convert all declared functions to lookup
    tables, allowing the system to solve for the values in the tables. 
    all tags are optional, save for the input file. It will generate output
    files in /tmp/ unless you give it an alternative.

    The resulting QF_BV input to your solver will be expanded by a constant
    factor in the overall length of your file times a constant factor of the
    length of your BitVecs. (Since BitVecs can be declared in a file of size
    logarithmic in their length, this is technically exponential expansion of
    the file.)

    you must supply an input file name as an argument
    
    -help or -h: brings up this dialogue 

    -QF_BV or -q: the filename to which to write the converted QF_BV version

    -solver or -s: the solver to apply to the result (NONE means don't solve)
                   default: z3

    -model or -m: the filename to which to write the solver's output

    -analysis or -a: if this tag is present, we execute analysis specific to 
                     Isaac's research. The remaining tags pertain to this.

    -html or -w: the html file to be opened in firefox and viewed.

    -iterative-optimize or -i: after solver has finished, try switching every
                               response that doesn't meet each threshold to 
                               meeting that threshold, one by one, testing
                               whether this violates the requirements, until
                               you either get one that works, and then start 
                               over, or get through all the response-threshold
                               pairs. In theory, this has the same basic effect
                               as -localmax, but it's slower, and less likely
                               to run out of memory and crash.

    -localmax or -l: requires from the solver that this set of thresholds is
                     locally maximal, which is to say that there there is no 
                     one set of possible responses which presently does not 
                     meet a threshold which can be made to meet that threshold
                     without violating a requirement.
                     DANGER: this option makes z3 (I don't know about other
                             solvers) take MUCH longer.
    
    """
        return 0
    iterative_optimize = False
    if "-iterative-optimize" in args:
        iterative_optimize = True
        args.pop(args.index("-iterative-optimize"))
    if "-i" in args:
        iterative_optimize = True
        args.pop(args.index("-i"))
    if "-analysis" in args:
        analysis = True
        args.pop(args.index("-analysis"))
    if "-a" in args:
        analysis = True
        args.pop(args.index("-a"))
    localmax = False
    if "-localmax" in args:
        localmax = True
        args.pop(args.index("-localmax"))
    if "-l" in args:
        localmax = True
        args.pop(args.index("-l"))
    now = str("-".join(str(datetime.datetime.now()).split()))
    html_filename = retrieve_and_remove(args, ["-html","-w"], "/tmp/pysmt-html-"+\
                                                              now+".html")
    model_filename= retrieve_and_remove(args, ["-model","-m"],"/tmp/pysmt-model-"\
                                                              +now+".smt2")
    qf_bv_filename = retrieve_and_remove(
        args, ["-QF-BV", "-qf-bv", "-qf_bv", "-QF_BV", "-q"],
        "/tmp/pysmt-QF_BV-" + now + ".smt2")
    solver = retrieve_and_remove(args, ["-solver", "-s"], "z3")
    input_filename = args[0]
    print "transforming " + input_filename + " to " + qf_bv_filename
    a, l = lisp_file_comments(input_filename, comment=";")
    remove_parens_around_tokens(l)
    declare_datatypes(a, l)
    define_const(l)
    declare_const(l)
    define_sort(l)
    define_fun_no_input(a, l)
    declare_fun_boolean_input(l)
    define_fun_boolean_input(l)
    functions_boolean_output_to_bv(a, l)
    bv_functions_to_tables(a, l)
    remove_parens_around_tokens(l)
    if localmax:
        local_max_thresholds(a, l)
    exists_replace(l)
    forall_single(l)
    forall_unroll(a, l)
    remove_parens_around_tokens(l)
    fix_get_model(a)
    fix_check_sat(a)
    prepend_headers(a, l)
    if analysis and (not iterative_optimize):
        n = get_n(l)
        append_analysis(n, a, l)
    qf_bv_file = open(qf_bv_filename, "w")
    for x in map(lambda x: str(x).strip(), a):
        if (len(x) > 0):
            if (x[0] == "("):
                x = LispNode(x).pretty_print()
            qf_bv_file.write(x)
            qf_bv_file.write("\n")
    qf_bv_file.flush()
    qf_bv_file.close()
    print "transformation complete: " + qf_bv_filename
    if solver != "NONE":
        print("attempting to apply solver to produce " + model_filename)
        os.system(solver + " " + qf_bv_filename + " > " + model_filename)
        model = open(model_filename, "r").read()
        model = model.strip()
        if model.startswith("sat"):
            print "sat"
            if iterative_optimize:
                m = LispNode(model[3:])
                iot = local_max_thresholds_iterative(a, l, m, solver)
                open(model_filename, "w").write("sat\n" + m.pretty_print())
                if analysis:
                    n = get_n(l)
                    append_analysis(n, a, l)
                    qf_bv_file = open(
                        "/tmp/iterative-analysis-final-" + now + ".smt2", "w")
                    for x in map(lambda x: str(x).strip(), a):
                        if (len(x) > 0):
                            if (x[0] == "("):
                                x = LispNode(x).pretty_print()
                                qf_bv_file.write(x)
                                qf_bv_file.write("\n")
                    qf_bv_file.flush()
                    qf_bv_file.close()
                    os.system(solver+" /tmp/iterative-analysis-final-"+now+".smt2"+" > "+\
                              "/tmp/iterative-analysis-model-"+now+".smt2")
                    model = open(
                        "/tmp/iterative-analysis-model-" + now + ".smt2",
                        "r").read()
                    model = model.strip()
            if analysis:
                n = get_n(l)
                print "analysis generating html to " + html_filename
                perform_analysis(n, LispNode(model[3:]), html_filename)
                print "done."
        elif model.startswith("unsat"):
            print "unsat"
        else:
            print "solver failed"
            print model
Example #16
0
def bv_functions_to_tables(all_lines_list, lispnode_list):
    """turns all declare-fun with bv inputs and outputs into defined functions
     that just look up stuff in a BV lookup value."""
    name_counter = 0
    for lispnode in lispnode_list:
        lispnode_index = lispnode_list.index(lispnode)
        all_lines_index = all_lines_list.index(lispnode)
        for declare_fun in lispnode.find("declare-fun"):
            # if this function's inputs and outputs are all bitvecs
            if len(declare_fun.parent.children) > 3 and\
               len(declare_fun.parent.children[3].children) > 2 and\
               declare_fun.parent.children[3].children[1] == "BitVec" and\
               str(declare_fun.parent.children[2]) != "()" and\
               reduce(lambda x,y:x and (not y.is_token) and len(y.children) > 2 and \
                                   y.children[1] == "BitVec",
                      declare_fun.parent.children[2].children, True):
                name = declare_fun.parent.children[1]
                args = LispNode(declare_fun.parent.children[2]).children
                sort = declare_fun.parent.children[3]
                arg_bits = reduce(lambda x, y: x + int(str(y.children[2])),
                                  args, 0)
                sort_bits = int(str(sort.children[2]))
                num_bits = (2**arg_bits) * sort_bits
                lispnode_list.insert(
                    lispnode_index,
                    LispNode("(declare-fun " + str(name) +
                             "-lookup-table () (_ BitVec " + str(num_bits) +
                             "))"))
                all_lines_list.insert(all_lines_index,
                                      lispnode_list[lispnode_index])
                declare_fun.token = "define-fun"
                for i in range(len(declare_fun.parent.children[2].children)):
                    declare_fun.parent.children[2].children[i] = LispNode(
                        parent=declare_fun.parent.children[2],
                        children=[
                            LispNode("generated-input-name-" + str(i)),
                            declare_fun.parent.children[2].children[i]
                        ])
                sort_bits_expressed = \
                    binaries(int(math.ceil(math.log((sort_bits+1), 2))))[sort_bits]
                sort_bits_expressed = LispNode(children=[
                    LispNode("concat"),
                    bv_all_zero_1_in_ith_place(
                        num_bits - len(sort_bits_expressed), num_bits),
                    LispNode("#b" + sort_bits_expressed)
                ])
                concat_args = reduce(
                    lambda z, w: LispNode(children=[
                        LispNode("concat"), z,
                        LispNode(w.children[0])
                    ]), declare_fun.parent.children[2].children[1:],
                    LispNode(declare_fun.parent.children[2].children[0].
                             children[0]))
                concat_args = LispNode(children=[
                    LispNode("concat"),
                    bv_all_zero_1_in_ith_place(num_bits -
                                               arg_bits, num_bits), concat_args
                ])
                declare_fun.parent.children.append(LispNode(
                    "((_ extract "+str(sort_bits-1)+" 0) (bvlshr "+str(name)+\
                    "-lookup-table (bvmul "+str(concat_args)+" "+\
                    str(sort_bits_expressed)+")))", parent = declare_fun.parent))
Example #17
0
def declare_datatypes(all_lines_list, lispnode_list):
    """ reads through the lispnode list, replacing all instances of
      declare-datatypes with no type arguments with finite value 
      declarations, as BitVectors.
      additionally, because it may be the case that this allows more
      values of the bitvector type than you enumerated, it inserts
      and clauses into all exists clauses of this type stating that
      the value must be at most the maximum possible replacement
      BitVec value. Forall statements are inserted for all functions
      returning values of this type stating that for all other inputs,
      the values returned by these functions must be at most the maximum
      possible replacment BitVec value.
      all_lines_list: the set of all the lines in the file
      lispnode_list: the set of just the lisp nodes in the file"""
    for lispnode in lispnode_list:
        for declare in lispnode.find(LispNode("declare-datatypes")):
            if declare.parent.children[1] == LispNode("()"):
                declare_index = all_lines_list.index(lispnode)
                declare_index_lispnodes = lispnode_list.index(lispnode)
                lispnode_list.remove(lispnode)
                all_lines_list.remove(lispnode)
                declare.parent.is_token = True  # this node becomes blank
                declare.parent.token = ""
                for datatype in declare.parent.children[2].children:
                    if len(datatype.children) > 1:
                        type_name = LispNode(datatype.children[0])
                        type_values = map(LispNode, datatype.children[1:])
                        n = int(math.ceil(math.log(len(type_values), 2)))
                        bins = map(lambda x: LispNode("#b" + x), binaries(n))
                        # we have to add a clause to all "exists" thingies
                        # and a forall for all functions that return these
                        for node in lispnode_list:
                            for type_name_instance in node.find(type_name):
                                if type_name_instance.parent != None:
                                    # if there's a delcared function that returns this type
                                    declare = type_name_instance.parent
                                    if len(declare.children) > 3:
                                        if declare.children[
                                                0] == "declare-fun":
                                            node_index = lispnode_list.index(
                                                node)
                                            node_all_index = all_lines_list.index(
                                                node)
                                            lispnode_list.insert(node_index+1,
                                              LispNode(children = [LispNode("assert"),
                                                LispNode(children = [LispNode("forall"),
                                                  LispNode(children = map(
                                                    lambda x:LispNode(children=[
                                                      LispNode("input-"+str(x)),
                                                      LispNode(declare.children[2].children[x])]),
                                                    range(len(declare.children[2].children)))),
                                                  LispNode("(bvule ("+str(declare.children[1])+" "+\
                                                   " ".join(map(lambda x:"input-"+str(x),
                                                     range(len(declare.children[2].children))))+") "+\
                                                   str(bins[len(type_values)-1])+")")])]))
                                            if lispnode_list[node_index+1].children[1].children[1] ==\
                                               "()":
                                                lispnode_list[node_index+1].children[1] = \
                                                    lispnode_list[node_index+1].children[1].children[2]
                                                lispnode_list[node_index+1].children[1].parent = \
                                                    lispnode_list[node_index+1]
                                            all_lines_list.insert(
                                                node_all_index + 1,
                                                lispnode_list[node_index + 1])
                                    # if there's an exists of this type
                                    if type_name_instance.parent.parent != None:
                                        if type_name_instance.parent.parent.parent != None:
                                            exists = type_name_instance.parent.parent.parent
                                            if len(exists.children) > 2:
                                                if exists.children[
                                                        0] == "exists":
                                                    exists.children[2] = LispNode(parent = exists,
                                                        children = [LispNode("and"),
                                                            LispNode("(bvule "+\
                                                            str(type_name_instance.parent.children[0])+\
                                                            " "+str(bins[len(type_values)-1])+")"), \
                                                            exists.children[2]])
                        for node in lispnode_list:
                            # replace the defined type with a bitvec
                            node.replace(type_name,
                                         LispNode("(_ BitVec " + str(n) + ")"))
                        for i in range(len(type_values)):
                            new_constant = LispNode("(define-const "+str(type_values[i])+\
                                                   " (_ BitVec "+str(n)+") "+str(bins[i])+")")
                            lispnode_list.insert(declare_index_lispnodes,
                                                 new_constant)
                            all_lines_list.insert(declare_index, new_constant)
Example #18
0
def declare_const(lispnode_list):
    "desugars declare-const into the declare-fun it always was"
    for lispnode in lispnode_list:
        for const in lispnode.find("declare-const"):
            const.token = "declare-fun"
            const.parent.children.insert(2, LispNode("()"))
Example #19
0
def local_max_thresholds(all_lines_list, lispnode_list):
  """adds the requirement that flipping any 1 of thresholds to 0 fails 
     assertions. Must be done after functions to tables but before 
     exists and forall unrolling"""
  all_requirements = LispNode(children = [LispNode("and")] + 
      map(lambda x:LispNode(x.children[1]), 
          filter(lambda y: (len(y.children) > 1) and (y.children[0]=="assert"),
                  lispnode_list)))
  # get the length of the lookup table, and thresholds functions
  len_lookup = 0
  thresholds_all_index = 0
  thresholds_index = 0
  thresholds_bv = LispNode("")
  thresholds = LispNode("")
  for lispnode in lispnode_list:
    for node in lispnode.find("thresholds-bv-version-lookup-table"):
      if node.parent.children[0] == "declare-fun":
        len_lookup = int(str(node.parent.children[3].children[2]))
    for node in lispnode.find("thresholds-bv-version"):
      if node.parent.children[0] == "define-fun":
        thresholds_bv = LispNode(node.parent)
    for node in lispnode.find("thresholds"):
      if node.parent.children[0] == "define-fun":
        thresholds_all_index = all_lines_list.index(lispnode)
        thresholds_index = lispnode_list.index(lispnode)
        thresholds = LispNode(node.parent)
  # create a new function, threshholds-with-table, that is like thresholds but
  # takes as its last input a lookup table
  new_thresholds = LispNode(thresholds)
  new_thresholds.replace("thresholds","thresholds-with-table")
  new_thresholds.replace("thresholds-bv-version","thresholds-bv-version-with-table")
  new_thresholds.children[2].children.append(LispNode(
      "(lookup-table (_ BitVec "+str(len_lookup)+"))", 
      parent = new_thresholds.children[2]))
  for ntbv in new_thresholds.find("thresholds-bv-version-with-table"):
    if ntbv.parent.children[0] == "thresholds-bv-version-with-table":
      ntbv.parent.children.append(LispNode("lookup-table", parent = ntbv))
  # create a function thresholds-bv-version-with-table for use by 
  # thresholds-with-table
  new_thresholds_bv = LispNode(thresholds_bv)
  new_thresholds_bv.replace("thresholds","thresholds-with-table")
  new_thresholds_bv.replace("thresholds-bv-version","thresholds-bv-version-with-table")
  new_thresholds_bv.children[2].children.append(LispNode(
      "(lookup-table (_ BitVec "+str(len_lookup)+"))", 
      parent = new_thresholds_bv.children[2]))
  new_thresholds_bv.children[4].replace("thresholds-bv-version-lookup-table",
                                        "lookup-table") 
  # modify the set of all requirements to take as input a table, and stipulate
  # that for no such table where you've changed one "1" bit to a "0" are all
  # the requirements met
  len_lookup_index = int(math.ceil(math.log(len_lookup, 2)))
  max_lookup_index = "#b"+binaries(len_lookup_index)[len_lookup-1]
  adapted_table = LispNode("(bvand thresholds-bv-version-lookup-table (bvnot "+\
      "(bvshl "+str(bv_all_zero_1_in_ith_place(len_lookup, 0))+" (concat "+\
      str(bv_all_zero_1_in_ith_place(len_lookup-len_lookup_index, len_lookup))+\
      " thresholds-lookup-index))))")
  for thresholds in all_requirements.find("thresholds"):
    if thresholds.parent.children[0] == thresholds:
      thresholds.token="thresholds-with-table"
      thresholds.parent.children.append(LispNode(adapted_table,
                                                 parent=thresholds.parent))
  lispnode_list.insert(thresholds_index, LispNode(children=
  [LispNode("assert"),
   LispNode(children = 
     [LispNode("not"),
      LispNode(children =
        [LispNode("exists"), 
         LispNode("((thresholds-lookup-index (_ BitVec "+str(len_lookup_index)+\
                    ")))"),
         LispNode(children=
           [LispNode("and"), 
            LispNode("(bvule thresholds-lookup-index "+max_lookup_index+")"),
            LispNode("(not (= thresholds-bv-version-lookup-table "+\
                              str(adapted_table)+"))"), 
            all_requirements])])])]))
  # add our functions to the file
  all_lines_list.insert(thresholds_all_index, lispnode_list[thresholds_index])
  lispnode_list.insert(thresholds_index, new_thresholds)
  all_lines_list.insert(thresholds_all_index, new_thresholds)
  lispnode_list.insert(thresholds_index, new_thresholds_bv)
  all_lines_list.insert(thresholds_all_index, new_thresholds_bv)
Example #20
0
def local_max_thresholds(all_lines_list, lispnode_list):
    """adds the requirement that flipping any 1 of thresholds to 0 fails 
     assertions. Must be done after functions to tables but before 
     exists and forall unrolling"""
    all_requirements = LispNode(children=[LispNode("and")] + map(
        lambda x: LispNode(x.children[1]),
        filter(lambda y: (len(y.children) > 1) and
               (y.children[0] == "assert"), lispnode_list)))
    # get the length of the lookup table, and thresholds functions
    len_lookup = 0
    thresholds_all_index = 0
    thresholds_index = 0
    thresholds_bv = LispNode("")
    thresholds = LispNode("")
    for lispnode in lispnode_list:
        for node in lispnode.find("thresholds-bv-version-lookup-table"):
            if node.parent.children[0] == "declare-fun":
                len_lookup = int(str(node.parent.children[3].children[2]))
        for node in lispnode.find("thresholds-bv-version"):
            if node.parent.children[0] == "define-fun":
                thresholds_bv = LispNode(node.parent)
        for node in lispnode.find("thresholds"):
            if node.parent.children[0] == "define-fun":
                thresholds_all_index = all_lines_list.index(lispnode)
                thresholds_index = lispnode_list.index(lispnode)
                thresholds = LispNode(node.parent)
    # create a new function, threshholds-with-table, that is like thresholds but
    # takes as its last input a lookup table
    new_thresholds = LispNode(thresholds)
    new_thresholds.replace("thresholds", "thresholds-with-table")
    new_thresholds.replace("thresholds-bv-version",
                           "thresholds-bv-version-with-table")
    new_thresholds.children[2].children.append(
        LispNode("(lookup-table (_ BitVec " + str(len_lookup) + "))",
                 parent=new_thresholds.children[2]))
    for ntbv in new_thresholds.find("thresholds-bv-version-with-table"):
        if ntbv.parent.children[0] == "thresholds-bv-version-with-table":
            ntbv.parent.children.append(LispNode("lookup-table", parent=ntbv))
    # create a function thresholds-bv-version-with-table for use by
    # thresholds-with-table
    new_thresholds_bv = LispNode(thresholds_bv)
    new_thresholds_bv.replace("thresholds", "thresholds-with-table")
    new_thresholds_bv.replace("thresholds-bv-version",
                              "thresholds-bv-version-with-table")
    new_thresholds_bv.children[2].children.append(
        LispNode("(lookup-table (_ BitVec " + str(len_lookup) + "))",
                 parent=new_thresholds_bv.children[2]))
    new_thresholds_bv.children[4].replace("thresholds-bv-version-lookup-table",
                                          "lookup-table")
    # modify the set of all requirements to take as input a table, and stipulate
    # that for no such table where you've changed one "1" bit to a "0" are all
    # the requirements met
    len_lookup_index = int(math.ceil(math.log(len_lookup, 2)))
    max_lookup_index = "#b" + binaries(len_lookup_index)[len_lookup - 1]
    adapted_table = LispNode("(bvand thresholds-bv-version-lookup-table (bvnot "+\
        "(bvshl "+str(bv_all_zero_1_in_ith_place(len_lookup, 0))+" (concat "+\
        str(bv_all_zero_1_in_ith_place(len_lookup-len_lookup_index, len_lookup))+\
        " thresholds-lookup-index))))")
    for thresholds in all_requirements.find("thresholds"):
        if thresholds.parent.children[0] == thresholds:
            thresholds.token = "thresholds-with-table"
            thresholds.parent.children.append(
                LispNode(adapted_table, parent=thresholds.parent))
    lispnode_list.insert(thresholds_index, LispNode(children=
    [LispNode("assert"),
     LispNode(children =
       [LispNode("not"),
        LispNode(children =
          [LispNode("exists"),
           LispNode("((thresholds-lookup-index (_ BitVec "+str(len_lookup_index)+\
                      ")))"),
           LispNode(children=
             [LispNode("and"),
              LispNode("(bvule thresholds-lookup-index "+max_lookup_index+")"),
              LispNode("(not (= thresholds-bv-version-lookup-table "+\
                                str(adapted_table)+"))"),
              all_requirements])])])]))
    # add our functions to the file
    all_lines_list.insert(thresholds_all_index,
                          lispnode_list[thresholds_index])
    lispnode_list.insert(thresholds_index, new_thresholds)
    all_lines_list.insert(thresholds_all_index, new_thresholds)
    lispnode_list.insert(thresholds_index, new_thresholds_bv)
    all_lines_list.insert(thresholds_all_index, new_thresholds_bv)
Example #21
0
def main(args):
  help_key = False
  analysis = False
  if len(args) < 1:
    help_key = True
  elif "-h" in args or "-help" in args:
    help_key = True
  if help_key:
    print """
    Pysmt: your friend when converting z3 SMT2 extensions and such to QF_BV
    If you have define-sort or declare-datatypes with no arguments, this may
    help you.
    Likewise, if you have functions with only BitVec or Boolean (or declared
    or defined datatypes) as inputs, this may help you.
    If you meet the above requirements, this may be able to convert your smt2
    file into a QF_BV smt2 file. It will unroll all quantifiers, replace all
    declared datatypes and defined sorts with what you defined them to be,
    replace all boolean function inputs with single bit bitvectors, (and fix
    any usage appropriately), and convert all declared functions to lookup
    tables, allowing the system to solve for the values in the tables. 
    all tags are optional, save for the input file. It will generate output
    files in /tmp/ unless you give it an alternative.

    The resulting QF_BV input to your solver will be expanded by a constant
    factor in the overall length of your file times a constant factor of the
    length of your BitVecs. (Since BitVecs can be declared in a file of size
    logarithmic in their length, this is technically exponential expansion of
    the file.)

    you must supply an input file name as an argument
    
    -help or -h: brings up this dialogue 

    -QF_BV or -q: the filename to which to write the converted QF_BV version

    -solver or -s: the solver to apply to the result (NONE means don't solve)
                   default: z3

    -model or -m: the filename to which to write the solver's output

    -analysis or -a: if this tag is present, we execute analysis specific to 
                     Isaac's research. The remaining tags pertain to this.

    -html or -w: the html file to be opened in firefox and viewed.

    -iterative-optimize or -i: after solver has finished, try switching every
                               response that doesn't meet each threshold to 
                               meeting that threshold, one by one, testing
                               whether this violates the requirements, until
                               you either get one that works, and then start 
                               over, or get through all the response-threshold
                               pairs. In theory, this has the same basic effect
                               as -localmax, but it's slower, and less likely
                               to run out of memory and crash.

    -localmax or -l: requires from the solver that this set of thresholds is
                     locally maximal, which is to say that there there is no 
                     one set of possible responses which presently does not 
                     meet a threshold which can be made to meet that threshold
                     without violating a requirement.
                     DANGER: this option makes z3 (I don't know about other
                             solvers) take MUCH longer.
    
    """
    return 0
  iterative_optimize = False
  if "-iterative-optimize" in args:
    iterative_optimize = True
    args.pop(args.index("-iterative-optimize"))
  if "-i" in args:
    iterative_optimize = True
    args.pop(args.index("-i"))
  if "-analysis" in args:
    analysis = True
    args.pop(args.index("-analysis"))
  if "-a" in args:
    analysis = True
    args.pop(args.index("-a"))
  localmax = False
  if "-localmax" in args:
    localmax = True
    args.pop(args.index("-localmax"))
  if "-l" in args:
    localmax = True
    args.pop(args.index("-l"))
  now = str("-".join(str(datetime.datetime.now()).split()))
  html_filename = retrieve_and_remove(args, ["-html","-w"], "/tmp/pysmt-html-"+\
                                                            now+".html")
  model_filename= retrieve_and_remove(args, ["-model","-m"],"/tmp/pysmt-model-"\
                                                            +now+".smt2")
  qf_bv_filename= retrieve_and_remove(args, ["-QF-BV","-qf-bv","-qf_bv",
      "-QF_BV","-q"], "/tmp/pysmt-QF_BV-"+now+".smt2")
  solver = retrieve_and_remove(args, ["-solver","-s"],"z3")
  input_filename = args[0]
  print "transforming "+input_filename+" to "+qf_bv_filename
  a, l = lisp_file_comments(input_filename, comment=";")
  remove_parens_around_tokens(l)
  declare_datatypes(a,l)
  define_const(l)
  declare_const(l)
  define_sort(l)
  define_fun_no_input(a,l)
  declare_fun_boolean_input(l)
  define_fun_boolean_input(l)
  functions_boolean_output_to_bv(a,l)
  bv_functions_to_tables(a,l)
  remove_parens_around_tokens(l)
  if localmax:
    local_max_thresholds(a,l)
  exists_replace(l)
  forall_single(l)
  forall_unroll(a,l)
  remove_parens_around_tokens(l)
  fix_get_model(a)
  fix_check_sat(a)
  prepend_headers(a,l)
  if analysis and (not iterative_optimize):
    n = get_n(l)
    append_analysis(n,a,l)
  qf_bv_file = open(qf_bv_filename,"w")
  for x in map(lambda x:str(x).strip(),a):
    if (len(x) > 0):
      if (x[0] == "("):
        x = LispNode(x).pretty_print()
      qf_bv_file.write(x)
      qf_bv_file.write("\n")
  qf_bv_file.flush() 
  qf_bv_file.close() 
  print "transformation complete: "+qf_bv_filename
  if solver != "NONE":
    print("attempting to apply solver to produce "+model_filename)
    os.system(solver+" "+qf_bv_filename+" > "+model_filename)
    model = open(model_filename,"r").read()
    model = model.strip()
    if model.startswith("sat"):
      print "sat"
      if iterative_optimize:
        m = LispNode(model[3:])
        iot = local_max_thresholds_iterative(a, l,m,solver)
        open(model_filename,"w").write("sat\n"+m.pretty_print())
        if analysis:
          n = get_n(l)
          append_analysis(n,a,l)
          qf_bv_file = open("/tmp/iterative-analysis-final-"+now+".smt2","w")
          for x in map(lambda x:str(x).strip(),a):
            if (len(x) > 0):
              if (x[0] == "("):
                x = LispNode(x).pretty_print()
                qf_bv_file.write(x)
                qf_bv_file.write("\n")
          qf_bv_file.flush() 
          qf_bv_file.close() 
          os.system(solver+" /tmp/iterative-analysis-final-"+now+".smt2"+" > "+\
                    "/tmp/iterative-analysis-model-"+now+".smt2")
          model = open("/tmp/iterative-analysis-model-"+now+".smt2","r").read()
          model = model.strip()
      if analysis:
        n = get_n(l)
        print "analysis generating html to "+html_filename
        perform_analysis(n,LispNode(model[3:]),html_filename)
        print "done."
    elif model.startswith("unsat"):
      print "unsat"
    else:
      print "solver failed"
      print model
Example #22
0
def forall_unroll(all_lines_list, lispnode_list):
    """Unroll any forall BitVec into a big and"""
    foralls_unrolled_so_far = 0
    lispnode_index = 0
    all_lines_index = 0
    while (lispnode_index < len(lispnode_list)):
        lispnode = lispnode_list[lispnode_index]
        while all_lines_list[all_lines_index] != lispnode:
            all_lines_index += 1
        for forall in lispnode.find("forall")[::-1]:  # innermost first
            enclosing_item_descriptions = \
                item_descriptions_from_enclosing_foralls(forall.parent)
            item_descriptions = forall.parent.children[1].children
            if reduce(
                    lambda x, y: x and (len(y.children[1].children) > 2) and y.
                    children[1].children[1] == "BitVec", item_descriptions,
                    True):
                #make a function representing this forall
                lispnode_list.insert(
                    lispnode_index,
                    LispNode(children=[
                        LispNode("define-fun"),
                        LispNode("forall-unroll-" +
                                 str(foralls_unrolled_so_far)),
                        LispNode(children=map(
                            LispNode, enclosing_item_descriptions +
                            item_descriptions)),
                        LispNode("Bool"), forall.parent.children[2]
                    ]))
                all_lines_list.insert(all_lines_index,
                                      lispnode_list[lispnode_index])
                lispnode_index += 1
                all_lines_index += 1
                forall.token = "and"
                forall.parent.children = [forall]
                call_forall_unroll = LispNode(children=[
                    LispNode("forall-unroll-" + str(foralls_unrolled_so_far))
                ] + map(lambda node: LispNode(node.children[0]),
                        enclosing_item_descriptions + item_descriptions))
                # I'm going to unroll this forall one bit at a time, so as
                # to guarantee program expansion linear in the size of the
                # largest bitvector
                if len(item_descriptions) > 0:
                    item_description = item_descriptions[0]
                    num_bits = int(
                        str(item_description.children[1].children[2]))
                    if num_bits == 0:
                        print "ERROR: 0 BIT BITVECTOR: " + str(forall.parent)
                    else:
                        for i in range(num_bits - 1):
                            lispnode_list.insert(lispnode_index, LispNode(children=
                              [LispNode("define-fun"),
                               LispNode("forall-unroll-"+str(foralls_unrolled_so_far)+\
                                        "-bit-"+str(i)),
                               LispNode(children = map(LispNode,
                                     enclosing_item_descriptions + item_descriptions)),
                               LispNode("Bool"),
                               LispNode(children=[LispNode("and"),
                                                  LispNode(call_forall_unroll),
                                                  LispNode(call_forall_unroll)])
                              ]))
                            all_lines_list.insert(all_lines_index, \
                                                  lispnode_list[lispnode_index])
                            # now I replace one of the arguments in that function with an
                            # argument at sets the ith bit to 1
                            lispnode_list[lispnode_index].children[4].children[
                                2].replace(
                                    item_description.children[0],
                                    LispNode(children=[
                                        LispNode("bvor"),
                                        item_description.children[0],
                                        bv_all_zero_1_in_ith_place(
                                            num_bits, i)
                                    ]))
                            call_forall_unroll = LispNode(children=
                              [LispNode("forall-unroll-"+str(foralls_unrolled_so_far)+\
                                        "-bit-"+str(i))]+
                               map(lambda node:LispNode(node.children[0]),
                                   enclosing_item_descriptions + item_descriptions))
                            lispnode_index += 1
                            all_lines_index += 1
                        forall.parent.children.append(
                            LispNode(call_forall_unroll, parent=forall.parent))
                        forall.parent.children.append(
                            LispNode(call_forall_unroll, parent=forall.parent))
                        forall.parent.children[1].replace(
                            item_description.children[0],
                            bv_all_zero_1_in_ith_place(num_bits, num_bits - 1))
                        forall.parent.children[2].replace(
                            item_description.children[0],
                            bv_all_zero_1_in_ith_place(num_bits, num_bits))
            foralls_unrolled_so_far += 1
        lispnode_index += 1
        all_lines_index += 1