Beispiel #1
0
class Randomizer(RandIF):
    """Implements the core randomization algorithm"""
    
    EN_DEBUG = False
    
    def __init__(self, debug=0):
        self.pretty_printer = ModelPrettyPrinter()
        self.debug = debug
    
    _state_p = [0,1]
    _rng = None
    
    def randomize(self, ri : RandInfo, bound_m : Dict[FieldModel,VariableBoundModel]):
        """Randomize the variables and constraints in a RandInfo collection"""
        

        if self.debug > 0:
            for rs in ri.randsets():
                print("RandSet")
                for f in rs.all_fields():
                    if f in bound_m.keys():
                        print("  Field: " + f.fullname + " " + str(bound_m[f].domain.range_l))
                for c in rs.constraints():
                    print("  Constraint: " + self.pretty_printer.do_print(c, show_exp=True))
            for uf in ri.unconstrained():
                print("Unconstrained: " + uf.name)
               
        # Assign values to the unconstrained fields first
        uc_rand = list(filter(lambda f:f.is_used_rand, ri.unconstrained()))
        for uf in uc_rand:
            if self.debug > 0:
                print("Randomizing unconstrained: " + uf.fullname)
            bounds = bound_m[uf]
            range_l = bounds.domain.range_l
            
            if len(range_l) == 1:
                # Single (likely domain-based) range
                uf.set_val(
                    self.randint(range_l[0][0], range_l[0][1]))
            else:
                # Most likely an enumerated type
                # TODO: are there any cases where these could be ranges?
                idx = self.randint(0, len(range_l)-1)
                uf.set_val(range_l[idx][0])                
                
            # Lock so we don't overwrite
            uf.set_used_rand(False)

        rs_i = 0
        start_rs_i = 0
        max_fields = 20
        while rs_i < len(ri.randsets()):        
            btor = Boolector()
            self.btor = btor
            btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
            btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)
            
            start_rs_i = rs_i

            constraint_l = []
            # Collect up to max_fields fields to randomize at a time
            n_fields = 0
            while rs_i < len(ri.randsets()):
                rs = ri.randsets()[rs_i]
                
                rs_node_builder = RandSetNodeBuilder(btor)

                all_fields = rs.all_fields()
                if self.debug > 0:
                    print("Pre-Randomize: RandSet")
                    for f in all_fields:
                        if f in bound_m.keys():
                            print("  Field: " + f.fullname + " " + str(bound_m[f].domain.range_l))
                    for c in rs.constraints():
                        print("  Constraint: " + self.pretty_printer.do_print(c, show_exp=True, print_values=True))

                rs_node_builder.build(rs)
                n_fields += len(all_fields)
                
                constraint_l.extend(list(map(lambda c:(c,c.build(btor),isinstance(c,ConstraintSoftModel)), rs.constraints())))
                
                rs_i += 1
                if n_fields > max_fields or rs.order != -1:
                    break
                
            for c in constraint_l:
                try:
                    btor.Assume(c[1])
                except Exception as e:
                    from ..visitors.model_pretty_printer import ModelPrettyPrinter
                    print("Exception: " + ModelPrettyPrinter.print(c[0]))
                    raise e
                
            soft_node_l = list(map(lambda c:c[1], filter(lambda c:c[2], constraint_l)))
            node_l = list(map(lambda c:c[1], filter(lambda c:not c[2], constraint_l)))

            # Perform an initial solve to establish correctness
            if btor.Sat() != btor.SAT:
                
                if len(soft_node_l) > 0:
                    # Try one more time before giving up
                    for i,f in enumerate(soft_node_l):
                        if btor.Failed(f):
                            soft_node_l[i] = None
                        
                    # Add back the hard-constraint nodes and soft-constraints that
                    # didn't fail                        
#                    for n in filter(lambda n:n is not None, node_l+soft_node_l):
                    for n in filter(lambda n:n is not None, node_l):
                        btor.Assume(n)
                        
                    for n in filter(lambda n:n is not None, soft_node_l):
                        btor.Assume(n)

                    # If we fail again, then we truly have a problem
                    if btor.Sat() != btor.SAT:
                    
                        # Ensure we clean up
#                        active_randsets = []
#                        x=start_rs_i
#                        while x < rs_i:
#                            rs = ri.randsets()[x]
#                            active_randsets.append(rs)
#                            for f in rs.all_fields():
#                                f.dispose()
#                            x += 1
                        active_randsets = []
                        for rs in ri.randsets():
                            active_randsets.append(rs)
                            for f in rs.all_fields():
                                f.dispose()
                            raise SolveFailure(
                                "solve failure",
                                self.create_diagnostics(active_randsets))

#                        raise SolveFailure(
#                            "solve failure", 
#                            self.create_diagnostics(active_randsets))
                    else:
                        # Still need to convert assumptions to assertions
                        for n in filter(lambda n:n is not None, node_l+soft_node_l):
                            btor.Assert(n)
                else:
#                    print("Failed constraints:")
#                    i=1
#                    for c in constraint_l:
#                        if btor.Failed(c[1]):
#                            print("[" + str(i) + "]: " + self.pretty_printer.do_print(c[0], False))
#                            print("[" + str(i) + "]: " + self.pretty_printer.do_print(c[0], True))
#                            i+=1
                            
                    # Ensure we clean up
                    active_randsets = []
                    for rs in ri.randsets():
                        active_randsets.append(rs)
                        for f in rs.all_fields():
                            f.dispose()
                    raise SolveFailure(
                        "solve failure",
                        self.create_diagnostics(active_randsets))
            else:
                # Still need to convert assumptions to assertions
                btor.Assert(*(node_l+soft_node_l))


            self.swizzle_randvars(btor, ri, start_rs_i, rs_i, bound_m)

        # Finalize the value of the field
            x = start_rs_i
            reset_v = DynamicExprResetVisitor()
            while x < rs_i:
                rs = ri.randsets()[x]
                for f in rs.all_fields():
                    f.post_randomize()
                    f.set_used_rand(False, 0)
                    f.dispose() # Get rid of the solver var, since we're done with it
                    f.accept(reset_v)
#                for f in rs.nontarget_field_s:
#                    f.dispose()
                for c in rs.constraints():
                    c.accept(reset_v)
                RandSetDisposeVisitor().dispose(rs)
                x += 1
                
                
        end = int(round(time.time() * 1000))


                    
    def swizzle_randvars(self, 
                btor     : Boolector, 
                ri       : RandInfo,
                start_rs : int,
                end_rs   : int,
                bound_m  : Dict[FieldModel,VariableBoundModel]):

        # TODO: we must ignore fields that are otherwise being controlled
        if self.debug > 0:
            print("--> swizzle_randvars")

        rand_node_l = []
        rand_e_l = []
        x=start_rs
        while x < end_rs:
            # For each random variable, select a partition with it's known 
            # domain and add the corresponding constraint
            rs = ri.randsets()[x]
            
            field_l = rs.rand_fields()
            
            if self.debug > 0:
                print("  " + str(len(field_l)) + " fields in randset")
            if len(field_l) == 1:
                # Go ahead and pick values in the domain, since there 
                # are no other constraints
                f = field_l[0]
                if f in bound_m.keys():
                    f_bound = bound_m[f]
                    if not f_bound.isEmpty():
                        e = self.create_rand_domain_constraint(f, f_bound)
                        if e is not None:
                            n = e.build(btor)
                            rand_node_l.append(n)
                            rand_e_l.append(e)
            elif len(field_l) > 0:
                field_idx = self.randint(0, len(field_l)-1)
                f = field_l[field_idx]
                if self.debug > 0:
                    print("Swizzling field " + f.name)
                if f in bound_m.keys():
                    f_bound = bound_m[f]
                 
                    if not f_bound.isEmpty():
                        e = self.create_rand_domain_constraint(f, f_bound)
                        if e is not None:
                            n = e.build(btor)
                            rand_node_l.append(n)
                            rand_e_l.append(e)
#                                btor.Assume(n)
                    else:
                        # It's always possible that this value is already fixed.
                        # Just ignore.
#                            rand_node_l.append(None)
                        pass
                else:
                    if self.debug > 0:
                        print("Note: no bounds found for field " + f.name)
            x += 1
            
        if len(rand_node_l) > 0:            
            btor.Assume(*rand_node_l)
#            btor.Assert(*rand_node_l)
     
            if btor.Sat() != btor.SAT:
                # Remove any failing assumptions
                
                if self.debug > 0:
                    print("Randomization constraint failed")
 
                n_failed = 0
                for i,n in enumerate(rand_node_l):
                    if n is not None and btor.Failed(n):
                        rand_node_l[i] = None
                        n_failed += 1

                # Sanity check                
                if n_failed == 0:
                    for e in rand_e_l:
                        print("Constraint: " + ModelPrettyPrinter.print(e))
                    raise Exception("UNSAT reported, but no failing assertions")
# 
                # Re-apply the constraints that succeeded
                btor.Assert(*filter(lambda n:n is not None, rand_node_l))
                if btor.Sat() != btor.SAT:
                    raise Exception("failed to add in randomization (2)")
        else:
            if btor.Sat() != btor.SAT:
                raise Exception("failed to add in randomization")
            
        if self.debug > 0:
            print("<-- swizzle_randvars")
                        
    def create_rand_domain_constraint(self, 
                f : FieldScalarModel, 
                bound_m : VariableBoundModel)->ExprModel:
        e = None
        range_l = bound_m.domain.range_l
        range_idx = self.randint(0, len(range_l)-1)
        range = range_l[range_idx]
        domain = range[1]-range[0]
        
        if self.debug > 0:
            print("create_rand_domain_constraint: " + f.name + " range_idx=" + str(range_idx) + " range=" + str(range))
        if domain > 64:
            r_type = self.randint(0, 3)
            single_val = self.randint(range[0], range[1])
                
            if r_type >= 0 and r_type <= 2: # range
                # Pretty simple. Partition and randomize
                bin_sz_h = 1 if int(domain/128) == 0 else int(domain/128)

                if r_type == 0:                
                    # Center value in bin
                    if single_val+bin_sz_h > range[1]:
                        max = range[1]
                        min = range[1]-2*bin_sz_h
                    elif single_val-bin_sz_h < range[0]:
                        max = range[0]+2*bin_sz_h
                        min = range[0]
                    else:
                        max = single_val+bin_sz_h
                        min = single_val-bin_sz_h
                elif r_type == 1:
                    # Bin starts at value
                    if single_val+2*bin_sz_h > range[1]:
                        max = range[1]
                        min = range[1]-2*bin_sz_h
                    elif single_val-2*bin_sz_h < range[0]:
                        max = range[0]+2*bin_sz_h
                        min = range[0]
                    else:
                        max = single_val+2*bin_sz_h
                        min = single_val
                elif r_type == 2:
                    # Bin ends at value
                    if single_val+2*bin_sz_h > range[1]:
                        max = range[1]
                        min = range[1]-2*bin_sz_h
                    elif single_val-2*bin_sz_h < range[0]:
                        max = range[0]+2*bin_sz_h
                        min = range[0]
                    else:
                        max = single_val
                        min = single_val-2*bin_sz_h
                 
                e = ExprBinModel(
                    ExprBinModel(
                        ExprFieldRefModel(f),
                        BinExprType.Ge,
                        ExprLiteralModel(
                            min,
                            f.is_signed, 
                            f.width)
                    ),
                    BinExprType.And,
                    ExprBinModel(
                        ExprFieldRefModel(f),
                        BinExprType.Le,
                        ExprLiteralModel(
                            max,
                            f.is_signed, 
                            f.width)
                    )
                )
            elif r_type == 3: # Single value
                e = ExprBinModel(
                        ExprFieldRefModel(f),
                        BinExprType.Eq,
                        ExprLiteralModel(single_val, f.is_signed, f.width))
        else:
            val = self.randint(range[0], range[1])
            e = ExprBinModel(
                ExprFieldRefModel(f),
                BinExprType.Eq,
                ExprLiteralModel(val, f.is_signed, f.width))

        return e
    
    def randint(self, low, high):
        if low > high:
            tmp = low
            low = high
            high = tmp
#        if Randomizer._rng is None:
#            Randomizer._rng = random.Random(random.randrange(sys.maxsize))
#        return Randomizer._rng.randint(low, high)
        return random.randint(low, high)
    
    def randbits(self, nbits):
#        if Randomizer._rng is None:
#            Randomizer._rng = random.Random(random.randrange(sys.maxsize))
#        return Randomizer._rng.randint(0, (1<<nbits)-1)
        return random.randint(0, (1<<nbits)-1)

    @staticmethod            
    def _next():
#        if Randomizer._rng is None:
#            Randomizer._rng = random.Random(random.randrange(sys.maxsize))
#        ret = Randomizer._rng.randint(0, 0xFFFFFFFF)
        ret = random.randint(0, 0xFFFFFFFF)
#         ret = (Randomizer._state_p[0] + Randomizer._state_p[1]) & 0xFFFFFFFF
#         Randomizer._state_p[1] ^= Randomizer._state_p[0]
#         Randomizer._state_p[0] = (((Randomizer._state_p[0] << 55) | (Randomizer._state_p[0] >> 9))
#             ^ Randomizer._state_p[1] ^ (Randomizer._state_p[1] << 14))
#         Randomizer._state_p[1] = (Randomizer._state_p[1] << 36) | (Randomizer._state_p[1] >> 28)
        
        return ret

    def create_diagnostics(self, active_randsets) -> str:
        ret = ""
        
        btor = Boolector()
        btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
        btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)
        
        diagnostic_constraint_l = [] 
        diagnostic_field_l = []
        
        # First, determine how many randsets are actually failing
        i = 0
        while i < len(active_randsets):
            rs = active_randsets[i]
            for f in rs.all_fields():
                f.build(btor)

            # Assume that we can omit all soft constraints, since they
            # will have already been omitted (?)                
            constraint_l = list(map(lambda c:(c,c.build(btor)), filter(lambda c:not isinstance(c,ConstraintSoftModel), rs.constraints())))
                
            for c in constraint_l:
                btor.Assume(c[1])

            if btor.Sat() != btor.SAT:
                # Save fields and constraints if the randset doesn't 
                # solve on its own
                diagnostic_constraint_l.extend(constraint_l)
                diagnostic_field_l.extend(rs.fields())
                
            i += 1

        problem_constraints = []
        solving_constraints = []
        # Okay, now perform a series of solves to identify
        # constraints that are actually a problem
        for c in diagnostic_constraint_l:
            btor.Assume(c[1])
            
            if btor.Sat() != btor.SAT:
                # This is a problematic constraint
                # Save it for later
                problem_constraints.append(c[0])
            else:
                # Not a problem. Assert it now
                btor.Assert(c[1])
                solving_constraints.append(c[0])
#                problem_constraints.append(c[0])
                
        if btor.Sat() != btor.SAT:
            raise Exception("internal error: system should solve")
        
        # Okay, we now have a constraint system that solves, and
        # a list of constraints that are a problem. We want to 
        # resolve the value of all variables referenced by the 
        # solving constraints so and then display the non-solving
        # constraints. This will (hopefully) help highlight the
        # reason for the failure
        for c in solving_constraints:
            c.accept(RefFieldsPostRandVisitor())

        ret += "Problem Constraints:\n"
        for i,pc in enumerate(problem_constraints):

            ret += "Constraint " + str(i+1) + ":\n"
            ret += ModelPrettyPrinter.print(pc, print_values=True)
            ret += ModelPrettyPrinter.print(pc, print_values=False)

        for rs in active_randsets:
            for f in rs.all_fields():
                f.dispose()
            
        return ret
            
        
    @staticmethod
    def do_randomize(
            field_model_l : List[FieldModel],
            constraint_l : List[ConstraintModel] = None,
            debug=0):
        # All fields passed to do_randomize are treated
        # as randomizable
#        if Randomizer._rng is None:
#            Randomizer._rng = random.Random(random.randrange(sys.maxsize))
#        seed = Randomizer._rng.randint(0, (1 << 64)-1)
        seed = random.randint(0, (1<<64)-1)
        
        for f in field_model_l:
            f.set_used_rand(True, 0)
           
        if debug > 0: 
            print("Initial Model:")        
            for fm in field_model_l:
                print("  " + ModelPrettyPrinter.print(fm))
                
        # First, invoke pre_randomize on all elements
        for fm in field_model_l:
            fm.pre_randomize()
            
        if constraint_l is None:
            constraint_l = []

        # Collect all variables (pre-array) and establish bounds            
        bounds_v = VariableBoundVisitor()
        bounds_v.process(field_model_l, constraint_l, False)

        # TODO: need to handle inline constraints that impact arrays
        constraints_len = len(constraint_l)
        for fm in field_model_l:
            constraint_l.extend(ArrayConstraintBuilder.build(
                fm, bounds_v.bound_m))
            # Now, handle dist constraints
            DistConstraintBuilder.build(seed, fm)
            
        for c in constraint_l:
            constraint_l.extend(ArrayConstraintBuilder.build(
                c, bounds_v.bound_m))
            # Now, handle dist constraints
            DistConstraintBuilder.build(seed, c)

        # If we made changes during array remodeling,
        # re-run bounds checking on the updated model
#        if len(constraint_l) != constraints_len:
        bounds_v.process(field_model_l, constraint_l)

        if debug > 0:
            print("Final Model:")        
            for fm in field_model_l:
                print("  " + ModelPrettyPrinter.print(fm))
            for c in constraint_l:
                print("  " + ModelPrettyPrinter.print(c, show_exp=True))
            

        r = Randomizer(debug=debug)
#        if Randomizer._rng is None:
#            Randomizer._rng = random.Random(random.randrange(sys.maxsize))
        ri = RandInfoBuilder.build(field_model_l, constraint_l, Randomizer._rng)
        try:
            r.randomize(ri, bounds_v.bound_m)
        finally:
            # Rollback any constraints we've replaced for arrays
            for fm in field_model_l:
                ConstraintOverrideRollbackVisitor.rollback(fm)
        
        for fm in field_model_l:
            fm.post_randomize()
Beispiel #2
0
class Randomizer(RandIF):
    """Implements the core randomization algorithm"""
    def __init__(self):
        self.pretty_printer = ModelPrettyPrinter()

    _state_p = [0, 1]
    _rng = random.Random()

    def randomize(self, ri: RandInfo, bound_m: Dict[FieldModel,
                                                    VariableBoundModel]):
        """Randomize the variables and constraints in a RandInfo collection"""

        #         for rs in ri.randsets():
        #             print("RandSet")
        #             for f in rs.all_fields():
        #                 print("  " + f.name + " " + str(bound_m[f].domain.range_l))
        #         for uf in ri.unconstrained():
        #             print("Unconstrained: " + uf.name)

        rs_i = 0
        start_rs_i = 0
        max_fields = 20
        while rs_i < len(ri.randsets()):
            btor = Boolector()
            self.btor = btor
            btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
            btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)

            start_rs_i = rs_i

            constraint_l = []
            # Collect up to max_fields fields to randomize at a time
            n_fields = 0
            while rs_i < len(ri.randsets()):
                rs = ri.randsets()[rs_i]
                try:
                    for f in rs.all_fields():
                        f.build(btor)
                        n_fields += 1
                except Exception as e:
                    for c in rs.constraints():
                        print("Constraint: " + self.pretty_printer.do_print(c))
                    raise e

                constraint_l.extend(
                    list(
                        map(
                            lambda c: (c, c.build(btor),
                                       isinstance(c, ConstraintSoftModel)),
                            rs.constraints())))

                rs_i += 1
                if n_fields > max_fields:
                    break

            for c in constraint_l:
                try:
                    btor.Assume(c[1])
                except Exception as e:
                    from ..visitors.model_pretty_printer import ModelPrettyPrinter
                    print("Exception: " + ModelPrettyPrinter.print(c[0]))
                    raise e

            soft_node_l = list(
                map(lambda c: c[1], filter(lambda c: c[2], constraint_l)))
            node_l = list(
                map(lambda c: c[1], filter(lambda c: not c[2], constraint_l)))

            # Perform an initial solve to establish correctness
            if btor.Sat() != btor.SAT:

                if len(soft_node_l) > 0:
                    # Try one more time before giving up
                    for i, f in enumerate(btor.Failed(*soft_node_l)):
                        if f:
                            soft_node_l[i] = None

                    # Add back the hard-constraint nodes and soft-constraints that
                    # didn't fail
                    for n in filter(lambda n: n is not None,
                                    node_l + soft_node_l):
                        btor.Assume(n)

                    # If we fail again, then we truly have a problem
                    if btor.Sat() != btor.SAT:

                        # Ensure we clean up
                        x = start_rs_i
                        while x < rs_i:
                            rs = ri.randsets()[x]
                            for f in rs.all_fields():
                                f.dispose()
                            x += 1

                        raise Exception("solve failure")
                    else:
                        # Still need to convert assumptions to assertions
                        for n in filter(lambda n: n is not None,
                                        node_l + soft_node_l):
                            btor.Assert(n)
                else:
                    print("Failed constraints:")
                    i = 1
                    for c in constraint_l:
                        if btor.Failed(c[1]):
                            print("[" + str(i) + "]: " +
                                  self.pretty_printer.do_print(c[0], False))
                            print("[" + str(i) + "]: " +
                                  self.pretty_printer.do_print(c[0], True))
                            i += 1

                    # Ensure we clean up
                    for rs in ri.randsets():
                        for f in rs.all_fields():
                            f.dispose()
                    print("Solve failure")
                    raise Exception("solve failure")
            else:
                # Still need to convert assumptions to assertions
                btor.Assert(*(node_l + soft_node_l))

            self.swizzle_randvars(btor, ri, start_rs_i, rs_i, bound_m)

            # Finalize the value of the field
            x = start_rs_i
            while x < rs_i:
                rs = ri.randsets()[x]
                for f in rs.all_fields():
                    f.post_randomize()
                    f.dispose(
                    )  # Get rid of the solver var, since we're done with it
                x += 1

        uc_rand = list(filter(lambda f: f.is_used_rand, ri.unconstrained()))
        for uf in uc_rand:
            bounds = bound_m[uf]
            range_l = bounds.domain.range_l

            if len(range_l) == 1:
                # Single (likely domain-based) range
                uf.set_val(self.randint(range_l[0][0], range_l[0][1]))
            else:
                # Most likely an enumerated type
                # TODO: are there any cases where these could be ranges?
                idx = self.randint(0, len(range_l) - 1)
                uf.set_val(range_l[idx][0])

    def randomize_1(self, ri: RandInfo, bound_m: Dict[FieldModel,
                                                      VariableBoundModel]):
        """Randomize the variables and constraints in a RandInfo collection"""

        #         for rs in ri.randsets():
        #             print("RandSet")
        #             for f in rs.all_fields():
        #                 print("  " + f.name + " " + str(bound_m[f].domain.range_l))
        #         for uf in ri.unconstrained():
        #             print("Unconstrained: " + uf.name)

        for rs in ri.randsets():
            #             print("RandSet:")
            #             for f in rs.fields():
            #                 print("  Field: " + f.name)
            #             for c in rs.constraints():
            #                 print("  Constraint: " + ModelPrettyPrinter.print(c))
            #             print("Randset: n_fields=" + str(len(rs.fields())))
            btor = Boolector()
            self.btor = btor
            btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
            btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)

            try:
                for f in rs.all_fields():
                    f.build(btor)
            except Exception as e:
                for c in rs.constraints():
                    print("Constraint: " + self.pretty_printer.do_print(c))
                raise e

            constraint_l = list(
                map(
                    lambda c:
                    (c, c.build(btor), isinstance(c, ConstraintSoftModel)),
                    rs.constraints()))

            for c in constraint_l:
                try:
                    btor.Assume(c[1])
                except Exception as e:
                    from ..visitors.model_pretty_printer import ModelPrettyPrinter
                    print("Exception: " + ModelPrettyPrinter.print(c[0]))
                    raise e

            soft_node_l = list(
                map(lambda c: c[1], filter(lambda c: c[2], constraint_l)))
            node_l = list(
                map(lambda c: c[1], filter(lambda c: not c[2], constraint_l)))
            #             soft_node_l : [BoolectorNode] = []
            #
            #             for c in rs.constraints():
            #                 try:
            #                     n = c.build(btor)
            #                     if isinstance(c, ConstraintSoftModel):
            #                         soft_node_l.append(n)
            #                     else:
            #                         node_l.append(n)
            # #                    btor.Assert(c.build(btor))
            #                     btor.Assume(n)
            #                 except Exception as e:
            #                     print("Error: The following constraint failed:\n" + str(c))
            #                     raise e

            # Perform an initial solve to establish correctness
            if btor.Sat() != btor.SAT:

                if len(soft_node_l) > 0:

                    # Try one more time before giving up
                    for i, f in enumerate(btor.Failed(*soft_node_l)):
                        if f:
                            soft_node_l[i] = None

                    # Add back the hard-constraint nodes and soft-constraints that
                    # didn't fail
                    for n in filter(lambda n: n is not None,
                                    node_l + soft_node_l):
                        btor.Assume(n)

                    # If we fail again, then we truly have a problem
                    if btor.Sat() != btor.SAT:

                        # Ensure we clean up
                        for f in rs.all_fields():
                            f.dispose()

                        raise Exception("solve failure")
                    else:
                        # Still need to convert assumptions to assertions
                        for n in filter(lambda n: n is not None,
                                        node_l + soft_node_l):
                            btor.Assert(n)
                else:
                    print("Failed constraints:")
                    i = 1
                    for c in constraint_l:
                        if btor.Failed(c[1]):
                            print("[" + str(i) + "]: " +
                                  self.pretty_printer.do_print(c[0], False))
                            print("[" + str(i) + "]: " +
                                  self.pretty_printer.do_print(c[0], True))
                            i += 1

                    # Ensure we clean up
                    for f in rs.all_fields():
                        f.dispose()
                    print("Solve failure")
                    raise Exception("solve failure")
            else:
                # Still need to convert assumptions to assertions
                btor.Assert(*(node_l + soft_node_l))

            self.swizzle_randvars_1(btor, rs, bound_m)

            # Finalize the value of the field
            for f in rs.all_fields():
                f.post_randomize()
                f.dispose(
                )  # Get rid of the solver var, since we're done with it

        uc_rand = list(filter(lambda f: f.is_used_rand, ri.unconstrained()))
        for uf in uc_rand:
            bounds = bound_m[uf]
            range_l = bounds.domain.range_l

            if len(range_l) == 1:
                # Single (likely domain-based) range
                uf.set_val(self.randint(range_l[0][0], range_l[0][1]))
            else:
                # Most likely an enumerated type
                # TODO: are there any cases where these could be ranges?
                idx = self.randint(0, len(range_l) - 1)
                uf.set_val(range_l[idx][0])

    def swizzle_randvars(self, btor: Boolector, ri: RandInfo, start_rs: int,
                         end_rs: int, bound_m: Dict[FieldModel,
                                                    VariableBoundModel]):

        # TODO: we must ignore fields that are otherwise being controlled

        rand_node_l = []
        x = start_rs
        while x < end_rs:
            # For each random variable, select a partition with it's known
            # domain and add the corresponding constraint
            rs = ri.randsets()[x]

            field_l = rs.fields_l()
            if len(field_l) == 1:
                # Go ahead and pick values in the domain, since there
                # are no other constraints
                f = field_l[0]
                e = self.create_single_var_domain_constraint(
                    field_l[0], bound_m[field_l[0]])

                if e is not None:
                    n = e.build(btor)
                    rand_node_l.append(n)
#                    btor.Assume(n)
            else:
                for f in field_l:
                    if f.is_used_rand and f in bound_m.keys():
                        f_bound = bound_m[f]

                        if not f_bound.isEmpty():
                            e = self.create_rand_domain_constraint(f, f_bound)
                            if e is not None:
                                n = e.build(btor)
                                rand_node_l.append(n)
#                                btor.Assume(n)
                        else:
                            # It's always possible that this value is already fixed.
                            # Just ignore.
                            #                            rand_node_l.append(None)
                            pass
            x += 1
        if len(rand_node_l) > 0:
            btor.Assume(*rand_node_l)

            if btor.Sat() != btor.SAT:
                # Remove any failing assumptions

                n_failed = 0
                for i, n in enumerate(rand_node_l):
                    if n is not None and btor.Failed(n):
                        rand_node_l[i] = None
                        n_failed += 1

                # Re-apply the constraints that succeeded
                btor.Assume(*filter(lambda n: n is not None, rand_node_l))

        if btor.Sat() != btor.SAT:
            raise Exception("failed to add in randomization")

    def swizzle_randvars_1(self, btor: Boolector, rs: RandInfo,
                           bound_m: Dict[FieldModel, VariableBoundModel]):

        # TODO: we must ignore fields that are otherwise being controlled

        # For each random variable, select a partition with it's known
        # domain and add the corresponding constraint
        rand_node_l = []

        field_l = list(rs.fields())
        if len(field_l) == 1:
            # Go ahead and pick values in the domain, since there
            # are no other constraints
            f = field_l[0]
            e = self.create_single_var_domain_constraint(
                field_l[0], bound_m[field_l[0]])

            if e is not None:
                n = e.build(btor)
                rand_node_l.append(n)
                btor.Assume(n)
        else:
            for f in field_l:
                if f.is_used_rand and f in bound_m.keys():
                    f_bound = bound_m[f]

                    if not f_bound.isEmpty():
                        e = self.create_rand_domain_constraint(f, f_bound)
                        if e is not None:
                            n = e.build(btor)
                            rand_node_l.append(n)
                            btor.Assume(n)
                    else:
                        # It's always possible that this value is already fixed.
                        # Just ignore.
                        rand_node_l.append(None)

        if btor.Sat() != btor.SAT:
            # Remove any failing assumptions

            n_failed = 0
            for i, n in enumerate(rand_node_l):
                if n is not None and btor.Failed(n):
                    rand_node_l[i] = None
                    n_failed += 1

            if btor.Sat() != btor.SAT:
                raise Exception("failed to add in randomization")
            else:
                btor.Assume(*filter(lambda n: n is not None, rand_node_l))

                if btor.Sat() != btor.SAT:
                    raise Exception("failed to add in randomization")

    def create_rand_domain_constraint(
            self, f: FieldScalarModel,
            bound_m: VariableBoundModel) -> ExprModel:
        e = None
        range_l = bound_m.domain.range_l
        #        print("create_rand_domain_constraint: " + f.name + " " + str(range_l))
        if len(range_l) == 1:
            domain = range_l[0][1] - range_l[0][0]
            if domain > 64:
                r_type = self.randint(0, 3)
                single_val = self.randint(range_l[0][0], range_l[0][1])

                if r_type >= 0 and r_type <= 2:  # range
                    # Pretty simple. Partition and randomize
                    bin_sz_h = 1 if int(domain / 128) == 0 else int(domain /
                                                                    128)

                    if r_type == 0:
                        # Center value in bin
                        if single_val + bin_sz_h > range_l[0][1]:
                            max = range_l[0][1]
                            min = range_l[0][1] - 2 * bin_sz_h
                        elif single_val - bin_sz_h < range_l[0][0]:
                            max = range_l[0][0] + 2 * bin_sz_h
                            min = range_l[0][0]
                        else:
                            max = single_val + bin_sz_h
                            min = single_val - bin_sz_h
                    elif r_type == 1:
                        # Bin starts at value
                        if single_val + 2 * bin_sz_h > range_l[0][1]:
                            max = range_l[0][1]
                            min = range_l[0][1] - 2 * bin_sz_h
                        elif single_val - 2 * bin_sz_h < range_l[0][0]:
                            max = range_l[0][0] + 2 * bin_sz_h
                            min = range_l[0][0]
                        else:
                            max = single_val + 2 * bin_sz_h
                            min = single_val
                    elif r_type == 2:
                        # Bin ends at value
                        if single_val + 2 * bin_sz_h > range_l[0][1]:
                            max = range_l[0][1]
                            min = range_l[0][1] - 2 * bin_sz_h
                        elif single_val - 2 * bin_sz_h < range_l[0][0]:
                            max = range_l[0][0] + 2 * bin_sz_h
                            min = range_l[0][0]
                        else:
                            max = single_val
                            min = single_val - 2 * bin_sz_h

                    e = ExprBinModel(
                        ExprBinModel(
                            ExprFieldRefModel(f), BinExprType.Ge,
                            ExprLiteralModel(min, f.is_signed, f.width)),
                        BinExprType.And,
                        ExprBinModel(
                            ExprFieldRefModel(f), BinExprType.Le,
                            ExprLiteralModel(max, f.is_signed, f.width)))
                elif r_type == 3:  # Single value
                    e = ExprBinModel(
                        ExprFieldRefModel(f), BinExprType.Eq,
                        ExprLiteralModel(single_val, f.is_signed, f.width))
            else:
                val = self.randint(0, domain - 1)
                e = ExprBinModel(ExprFieldRefModel(f), BinExprType.Eq,
                                 ExprLiteralModel(val, f.is_signed, f.width))
        else:
            #             domain_bin_ratio = int(len(bound_m.domain.range_l)/len(bound_m.domain_offsets))
            #
            #             if domain_bin_ratio <= 1:
            #                 # Just pick a single value
            #                 print("Should pick single value")
            #             else:
            #                 #
            #                 print("domain_bin_ratio=" + str(domain_bin_ratio))
            #                 pass
            #             # Multi-range domain
            pass

        return e

    def create_single_var_domain_constraint(
            self, f: FieldScalarModel,
            bound_m: VariableBoundModel) -> ExprModel:
        range_l = bound_m.domain.range_l
        if len(range_l) == 1:
            val = self.randint(range_l[0][0], range_l[0][1])
            e = ExprBinModel(ExprFieldRefModel(f), BinExprType.Eq,
                             ExprLiteralModel(val, f.is_signed, f.width))
            return e
        else:
            #            domain_bin_ratio = int(len(bound_m.domain.range_l)/len(bound_m.domain_offsets))
            domain_bin_ratio = 1

            if domain_bin_ratio <= 1:
                # Just pick a single value
                off_val = self.randint(0, bound_m.domain_sz - 1)
                target_val = bound_m.offset2value(off_val)
                e = ExprBinModel(
                    ExprFieldRefModel(f), BinExprType.Eq,
                    ExprLiteralModel(target_val, f.is_signed, f.width))
                return e
            else:
                # TODO: For a variable with a small number of bins
                # relative to the domain the cover, it likely makes
                # sense to try to place a range within the bin instead
                # of selecting a single value
                #
                print("domain_bin_ratio=" + str(domain_bin_ratio))
                pass

            return None

#         gd = f.randgen_data
#
#         if gd.bag is not None:
#             # Pick out of the bag
#             vi = self.randint(0, len(gd.bag)-1)
#             e = ExprBinModel(
#                 ExprFieldRefModel(f),
#                 BinExprType.Eq,
#                 ExprLiteralModel(gd.bag[vi], f.is_signed, f.width))
#         else:
#             domain = (gd.max-gd.min+1)
#
#             if domain > 64:
#                 # Bin randomization
#                 bin = self.randint(0, 100)
#                 bin_sz = int(domain/100)
#                 e = ExprBinModel(
#                     ExprBinModel(
#                         ExprFieldRefModel(f),
#                         BinExprType.Ge,
#                         ExprLiteralModel(gd.min+bin_sz*bin, f.is_signed, f.width)
#                     ),
#                     BinExprType.And,
#                     ExprBinModel(
#                         ExprFieldRefModel(f),
#                         BinExprType.Le,
#                         ExprLiteralModel(gd.min+bin_sz*(bin+1)-1, f.is_signed, f.width)
#                         )
#                 )
#             else:
#                 # Select a specific value
#                 off = self.randint(0, domain-1)
#                 e = ExprBinModel(
#                     ExprFieldRefModel(f),
#                     BinExprType.Eq,
#                     ExprLiteralModel(gd.min+off, f.is_signed, f.width)
#                     )

    def calc_domain(self, f: FieldScalarModel, btor: Boolector):
        """Find the reachable bounds of a variable"""
        gd = f.randgen_data
        mid = gd.min + (gd.max - gd.min - 1)

        # Find the minimum
        #        while mid > self.min and mid < self.max:
        #

        pass

    def swizzle_randvars_slice(self, btor, rs):
        # Form the swizzle expression around bits in the
        # target randset variables. The resulting expression
        # enables us to minimize the deviations from the selected
        # bit values
        rand_node_l = []
        for f in rs.fields():
            val = self.randbits(f.width)
            for i in range(f.width):
                bit_i = ((val >> i) & 1)
                n = btor.Eq(btor.Slice(f.var, i, i), btor.Const(bit_i, 1))
                rand_node_l.append(n)
        btor.Assume(*rand_node_l)

        if btor.Sat() != btor.SAT:

            # Clear out any failing assumptions

            # Try one more time before giving up
            n_failed = 0
            for i, f in enumerate(rand_node_l):
                if btor.Failed(f):
                    rand_node_l[i] = None
                    n_failed += 1

            btor.Assume(*filter(lambda n: n is not None, rand_node_l))

            if btor.Sat() != btor.SAT:
                print("Randomization failed")
                for i, n in enumerate(rand_node_l):
                    if n is not None:
                        if btor.Failed(n):
                            print("Assumption " + str(i) + " failed")
                raise Exception("solve failure")

    def minimize(self, expr, min_t, max_t):
        ret = -1

        if min_t == max_t:
            mid_point = min_t
        else:
            mid_point = min_t + int((max_t - min_t + 1) / 2)
#        print("--> optimize_rand_c: min=" + str(min_t) + " mid_point=" +  str(mid_point) + " max=" + str(max_t))

# Push a new constraint scope
#        self.btor.Push()

        self.btor.Assume(self.btor.Ulte(expr, self.btor.Const(mid_point, 32)))

        if self.btor.Sat() == self.btor.SAT:
            #            print("  SAT")
            if mid_point > 0 and min_t != max_t:
                #                self.btor.Pop()
                # Continue making the range smaller
                sub_r = self.minimize(expr, min_t, mid_point - 1)
                if sub_r == -1:
                    # re-solve, since this is the best we'll do
                    self.btor.Push()
                    self.btor.Sat()
                    ret = mid_point
                else:
                    # The sub-solved worked out, so take that value
                    ret = sub_r
            else:
                ret = mid_point
        else:
            #            print("  UNSAT")
            #            self.btor.Pop()
            if mid_point < max_t:
                # Solve failed, so let's explore the upper portion
                ret = self.minimize(expr, mid_point + 1, max_t)
            else:
                # Dead-end here
                ret = -1

#        print("<-- optimize_rand_c: ret=" + str(ret))

        return ret

    def randint(self, low, high):
        return Randomizer._rng.randint(low, high)

    def randbits(self, nbits):
        return Randomizer._rng.randint(0, (1 << nbits) - 1)

    @staticmethod
    def _next():
        ret = Randomizer._rng.randint(0, 0xFFFFFFFF)
        #         ret = (Randomizer._state_p[0] + Randomizer._state_p[1]) & 0xFFFFFFFF
        #         Randomizer._state_p[1] ^= Randomizer._state_p[0]
        #         Randomizer._state_p[0] = (((Randomizer._state_p[0] << 55) | (Randomizer._state_p[0] >> 9))
        #             ^ Randomizer._state_p[1] ^ (Randomizer._state_p[1] << 14))
        #         Randomizer._state_p[1] = (Randomizer._state_p[1] << 36) | (Randomizer._state_p[1] >> 28)

        return ret

    @staticmethod
    def do_randomize(field_model_l: List[FieldModel],
                     constraint_l: List[ConstraintModel] = None):
        # All fields passed to do_randomize are treated
        # as randomizable
        for f in field_model_l:
            f.set_used_rand(True, 0)

        if constraint_l is None:
            constraint_l = []

        # Collect all variables (pre-array) and establish bounds
        bounds_v = VariableBoundVisitor()
        bounds_v.process(field_model_l, constraint_l, False)

        # TODO: need to handle inline constraints that impact arrays
        constraints_len = len(constraint_l)
        for fm in field_model_l:
            constraint_l.extend(
                ArrayConstraintBuilder.build(fm, bounds_v.bound_m))

        # If we made changes during array remodeling,
        # re-run bounds checking on the updated model
#        if len(constraint_l) != constraints_len:
        bounds_v.process(field_model_l, constraint_l)

        # First, invoke pre_randomize on all elements
        for fm in field_model_l:
            fm.pre_randomize()

        r = Randomizer()
        ri = RandInfoBuilder.build(field_model_l, constraint_l,
                                   Randomizer._rng)
        try:
            r.randomize(ri, bounds_v.bound_m)
        finally:
            # Rollback any constraints we've replaced for arrays
            for fm in field_model_l:
                ConstraintOverrideRollbackVisitor.rollback(fm)

        for fm in field_model_l:
            fm.post_randomize()
Beispiel #3
0
class Randomizer(RandIF):
    """Implements the core randomization algorithm"""

    EN_DEBUG = False

    def __init__(self,
                 randstate,
                 debug=0,
                 lint=0,
                 solve_fail_debug=0,
                 solve_info=None):
        self.randstate = randstate
        self.pretty_printer = ModelPrettyPrinter()
        self.solve_info = solve_info
        self.debug = debug
        if glbl_debug > 0 and glbl_debug > debug:
            self.debug = glbl_debug

        self.lint = lint
        self.solve_fail_debug = solve_fail_debug
        if glbl_solvefail_debug > 0 and glbl_solvefail_debug > solve_fail_debug:
            self.solve_fail_debug = glbl_solvefail_debug

#        self.swizzler = SolveGroupSwizzlerRange(solve_info)
        self.swizzler = SolveGroupSwizzlerPartsel(randstate, solve_info)

    _state_p = [0, 1]
    _rng = None

    def randomize(self, ri: RandInfo, bound_m: Dict[FieldModel,
                                                    VariableBoundModel]):
        """Randomize the variables and constraints in a RandInfo collection"""

        if self.solve_info is not None:
            self.solve_info.n_randsets = len(ri.randsets())

        if self.debug > 0:
            rs_i = 0
            while rs_i < len(ri.randsets()):
                rs = ri.randsets()[rs_i]
                print("RandSet[%d]" % rs_i)
                for f in rs.all_fields():
                    if f in bound_m.keys():
                        print("  Field: %s is_rand=%s %s" %
                              (f.fullname, str(f.is_used_rand),
                               str(bound_m[f].domain.range_l)))
                    else:
                        print("  Field: %s is_rand=%s (unbounded)" %
                              (f.fullname, str(f.is_used_rand)))

                for c in rs.constraints():
                    print("  Constraint: " +
                          self.pretty_printer.do_print(c, show_exp=True))
                for c in rs.soft_constraints():
                    print("  SoftConstraint: " +
                          self.pretty_printer.do_print(c, show_exp=True))

                rs_i += 1

            for uf in ri.unconstrained():
                print("Unconstrained: " + uf.fullname)

        # Assign values to the unconstrained fields first
        uc_rand = list(filter(lambda f: f.is_used_rand, ri.unconstrained()))
        for uf in uc_rand:
            if self.debug > 0:
                print("Randomizing unconstrained: " + uf.fullname)
            bounds = bound_m[uf]
            range_l = bounds.domain.range_l

            if len(range_l) == 1:
                # Single (likely domain-based) range
                uf.set_val(self.randstate.randint(range_l[0][0],
                                                  range_l[0][1]))
            else:
                # Most likely an enumerated type
                # TODO: are there any cases where these could be ranges?
                idx = self.randstate.randint(0, len(range_l) - 1)
                uf.set_val(range_l[idx][0])

            # Lock so we don't overwrite
            uf.set_used_rand(False)

        rs_i = 0
        start_rs_i = 0
        #        max_fields = 20
        max_fields = 0
        while rs_i < len(ri.randsets()):
            btor = Boolector()
            self.btor = btor
            btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
            btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)

            start_rs_i = rs_i

            constraint_l = []
            soft_constraint_l = []

            # Collect up to max_fields fields to randomize at a time
            n_fields = 0
            while rs_i < len(ri.randsets()):
                rs = ri.randsets()[rs_i]

                rs_node_builder = RandSetNodeBuilder(btor)

                all_fields = rs.all_fields()
                if self.debug > 0:
                    print("Pre-Randomize: RandSet[%d]" % rs_i)
                    for f in all_fields:
                        if f in bound_m.keys():
                            print("  Field: %s is_rand=%s %s var=%s" %
                                  (f.fullname, str(f.is_used_rand),
                                   str(bound_m[f].domain.range_l), str(f.var)))
                        else:
                            print("  Field: %s is_rand=%s (unbounded)" %
                                  (f.fullname, str(f.is_used_rand)))
                    for c in rs.constraints():
                        print("  Constraint: " + self.pretty_printer.do_print(
                            c, show_exp=True, print_values=True))
                    for c in rs.soft_constraints():
                        print("  SoftConstraint: " +
                              self.pretty_printer.do_print(
                                  c, show_exp=True, print_values=True))

                if self.solve_info is not None:
                    self.solve_info.n_cfields += len(all_fields)

                rs_node_builder.build(rs)
                n_fields += len(all_fields)

                #                constraint_l.extend(list(map(lambda c:(c,c.build(btor),isinstance(c,ConstraintSoftModel)), rs.constraints())))
                constraint_l.extend(
                    list(map(lambda c: (c, c.build(btor)), rs.constraints())))
                soft_constraint_l.extend(
                    list(
                        map(lambda c: (c, c.build(btor)),
                            rs.soft_constraints())))

                # Sort the list in descending order so we know which constraints
                # to prioritize
                soft_constraint_l.sort(key=lambda c: c[0].priority,
                                       reverse=True)

                rs_i += 1
                if n_fields > max_fields or rs.order != -1:
                    break

            for c in constraint_l:
                try:
                    btor.Assume(c[1])
                except Exception as e:
                    print("Exception: " + self.pretty_printer.print(c[0]))
                    raise e

            if self.solve_info is not None:
                self.solve_info.n_sat_calls += 1

            if btor.Sat() != btor.SAT:
                # If the system doesn't solve with hard constraints added,
                # then we may as well bail now
                active_randsets = []
                for rs in ri.randsets():
                    active_randsets.append(rs)
                    for f in rs.all_fields():
                        f.dispose()

                if self.solve_fail_debug > 0:
                    raise SolveFailure(
                        "solve failure",
                        self.create_diagnostics(active_randsets))
                else:
                    raise SolveFailure(
                        "solve failure",
                        "Solve failure: set 'solve_fail_debug=1' for more details"
                    )
            else:
                # Lock down the hard constraints that are confirmed
                # to be valid
                for c in constraint_l:
                    btor.Assert(c[1])

            # If there are soft constraints, add these now
            if len(soft_constraint_l) > 0:
                for c in soft_constraint_l:
                    try:
                        btor.Assume(c[1])
                    except Exception as e:
                        from ..visitors.model_pretty_printer import ModelPrettyPrinter
                        print("Exception: " + ModelPrettyPrinter.print(c[0]))
                        raise e

                if self.solve_info is not None:
                    self.solve_info.n_sat_calls += 1
                if btor.Sat() != btor.SAT:
                    # All the soft constraints cannot be satisfied. We'll need to
                    # add them incrementally
                    if self.debug > 0:
                        print(
                            "Note: some of the %d soft constraints could not be satisfied"
                            % len(soft_constraint_l))

                    for c in soft_constraint_l:
                        btor.Assume(c[1])

                        if self.solve_info is not None:
                            self.solve_info.n_sat_calls += 1
                        if btor.Sat() == btor.SAT:
                            if self.debug > 0:
                                print("Note: soft constraint %s (%d) passed" %
                                      (self.pretty_printer.print(
                                          c[0]), c[0].priority))
                            btor.Assert(c[1])
                        else:
                            if self.debug > 0:
                                print("Note: soft constraint %s (%d) failed" %
                                      (self.pretty_printer.print(
                                          c[0]), c[0].priority))
                else:
                    # All the soft constraints could be satisfied. Assert them now
                    if self.debug > 0:
                        print(
                            "Note: all %d soft constraints could be satisfied"
                            % len(soft_constraint_l))
                    for c in soft_constraint_l:
                        btor.Assert(c[1])

#            btor.Sat()
            x = start_rs_i
            while x < rs_i:
                self.swizzler.swizzle(btor, ri.randsets()[x], bound_m)
                x += 1

            # Finalize the value of the field
            x = start_rs_i
            reset_v = DynamicExprResetVisitor()
            while x < rs_i:
                rs = ri.randsets()[x]
                for f in rs.all_fields():
                    f.post_randomize()
                    f.set_used_rand(False, 0)
                    f.dispose(
                    )  # Get rid of the solver var, since we're done with it
                    f.accept(reset_v)
#                for f in rs.nontarget_field_s:
#                    f.dispose()
                for c in rs.constraints():
                    c.accept(reset_v)
                RandSetDisposeVisitor().dispose(rs)

                if self.debug > 0:
                    print("Post-Randomize: RandSet[%d]" % x)
                    for f in all_fields:
                        if f in bound_m.keys():
                            print("  Field: %s %s" %
                                  (f.fullname, str(f.val.val)))
                        else:
                            print("  Field: %s (unbounded) %s" %
                                  (f.fullname, str(f.val.val)))

                    for c in rs.constraints():
                        print("  Constraint: " + self.pretty_printer.do_print(
                            c, show_exp=True, print_values=True))
                    for c in rs.soft_constraints():
                        print("  SoftConstraint: " +
                              self.pretty_printer.do_print(
                                  c, show_exp=True, print_values=True))

                x += 1

        end = int(round(time.time() * 1000))

    def create_diagnostics_1(self, active_randsets) -> str:
        ret = ""

        btor = Boolector()
        btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
        btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)
        model_valid = False

        diagnostic_constraint_l = []
        diagnostic_field_l = []

        # First, determine how many randsets are actually failing
        i = 0
        while i < len(active_randsets):
            rs = active_randsets[i]
            for f in rs.all_fields():
                f.build(btor)

            # Assume that we can omit all soft constraints, since they
            # will have already been omitted (?)
            constraint_l = list(
                map(
                    lambda c: (c, c.build(btor)),
                    filter(lambda c: not isinstance(c, ConstraintSoftModel),
                           rs.constraints())))

            for c in constraint_l:
                btor.Assume(c[1])

            if btor.Sat() != btor.SAT:
                # Save fields and constraints if the randset doesn't
                # solve on its own
                diagnostic_constraint_l.extend(constraint_l)
                diagnostic_field_l.extend(rs.fields())

            i += 1

        problem_constraints = []
        solving_constraints = []
        # Okay, now perform a series of solves to identify
        # constraints that are actually a problem
        for c in diagnostic_constraint_l:
            btor.Assume(c[1])
            model_valid = False

            if btor.Sat() != btor.SAT:
                # This is a problematic constraint
                # Save it for later
                problem_constraints.append(c[0])
            else:
                # Not a problem. Assert it now
                btor.Assert(c[1])
                solving_constraints.append(c[0])
                model_valid = True
#                problem_constraints.append(c[0])

        if btor.Sat() != btor.SAT:
            raise Exception("internal error: system should solve")

        # Okay, we now have a constraint system that solves, and
        # a list of constraints that are a problem. We want to
        # resolve the value of all variables referenced by the
        # solving constraints so and then display the non-solving
        # constraints. This will (hopefully) help highlight the
        # reason for the failure
        for c in solving_constraints:
            c.accept(RefFieldsPostRandVisitor())

        ret += "Problem Constraints:\n"
        for i, pc in enumerate(problem_constraints):

            ret += "Constraint %d: %s\n" % (i, SourceInfo.toString(pc.srcinfo))
            ret += ModelPrettyPrinter.print(pc, print_values=True)
            ret += ModelPrettyPrinter.print(pc, print_values=False)

        for rs in active_randsets:
            for f in rs.all_fields():
                f.dispose()

        return ret

    def create_diagnostics(self, active_randsets) -> str:

        btor = Boolector()
        btor.Set_opt(pyboolector.BTOR_OPT_INCREMENTAL, True)
        btor.Set_opt(pyboolector.BTOR_OPT_MODEL_GEN, True)
        model_valid = False

        diagnostic_constraint_l = []
        diagnostic_field_l = []

        # First, determine how many randsets are actually failing
        i = 0
        while i < len(active_randsets):
            rs = active_randsets[i]
            for f in rs.all_fields():
                f.build(btor)

            # Assume that we can omit all soft constraints, since they
            # will have already been omitted (?)
            constraint_l = list(
                map(
                    lambda c: (c, c.build(btor)),
                    filter(lambda c: not isinstance(c, ConstraintSoftModel),
                           rs.constraints())))

            for c in constraint_l:
                btor.Assume(c[1])

            if btor.Sat() != btor.SAT:
                # Save fields and constraints if the randset doesn't
                # solve on its own
                diagnostic_constraint_l.extend(constraint_l)
                diagnostic_field_l.extend(rs.fields())

            i += 1

        problem_sets = []
        degree = 1

        while True:
            init_size = len(diagnostic_constraint_l)
            tmp_l = []

            ret = self._collect_failing_constraints(btor,
                                                    diagnostic_constraint_l, 0,
                                                    degree, tmp_l,
                                                    problem_sets)

            if len(diagnostic_constraint_l) == init_size and degree > 3:
                break
            else:
                degree += 1

        if Randomizer.EN_DEBUG > 0:
            print("%d constraints remaining ; %d problem sets" %
                  (len(diagnostic_constraint_l), len(problem_sets)))

        # Assert the remaining constraints
        for c in diagnostic_constraint_l:
            btor.Assert(c[1])

        if btor.Sat() != btor.SAT:
            raise Exception("internal error: system should solve")

        # Okay, we now have a constraint system that solves, and
        # a list of constraints that are a problem. We want to
        # resolve the value of all variables referenced by the
        # solving constraints so and then display the non-solving
        # constraints. This will (hopefully) help highlight the
        # reason for the failure

        ret = ""
        for ps in problem_sets:
            ret += ("Problem Set: %d constraints\n" % len(ps))
            for pc in ps:
                ret += "  %s:\n" % SourceInfo.toString(pc[0].srcinfo)
                ret += "    %s" % ModelPrettyPrinter.print(pc[0],
                                                           print_values=False)

            pc = []
            for c in ps:
                pc.append(c[0])

            lint_r = LintVisitor().lint([], pc)

            if lint_r != "":
                ret += "Lint Results:\n" + lint_r

        for rs in active_randsets:
            for f in rs.all_fields():
                f.dispose()

        return ret

    def _collect_failing_constraints(self, btor, src_constraint_l, idx, max,
                                     tmp_l, fail_set_l):
        ret = False
        if len(tmp_l) < max:
            i = idx
            while i < len(src_constraint_l):
                tmp_l.append(i)
                ret = self._collect_failing_constraints(
                    btor, src_constraint_l, i + 1, max, tmp_l, fail_set_l)
                tmp_l.pop()
                if ret:
                    src_constraint_l.pop(i)
                else:
                    i += 1
        else:
            # Assume full set of collected constraints
            if Randomizer.EN_DEBUG:
                print("Assume: " + str(tmp_l))
            for c in tmp_l:
                btor.Assume(src_constraint_l[c][1])
            if btor.Sat() != btor.SAT:
                # Set failed. Add to fail_set
                fail_s = []
                for ci in tmp_l:
                    fail_s.append(src_constraint_l[ci])
                fail_set_l.append(tuple(fail_s))
                ret = True

        return ret

    @staticmethod
    def do_randomize(randstate,
                     srcinfo: SourceInfo,
                     field_model_l: List[FieldModel],
                     constraint_l: List[ConstraintModel] = None,
                     debug=0,
                     lint=0,
                     solve_fail_debug=0):
        if profile_on():
            solve_info = SolveInfo()
            solve_info.totaltime = time.time()
            randomize_start(srcinfo, field_model_l, constraint_l)
        else:
            solve_info = None

        clear_soft_priority = ClearSoftPriorityVisitor()

        for f in field_model_l:
            f.set_used_rand(True, 0)
            clear_soft_priority.clear(f)

        if debug > 0:
            print("Initial Model:")
            for fm in field_model_l:
                print("  " + ModelPrettyPrinter.print(fm))

        # First, invoke pre_randomize on all elements
        for fm in field_model_l:
            fm.pre_randomize()

        if constraint_l is None:
            constraint_l = []

        for c in constraint_l:
            clear_soft_priority.clear(c)

        # Collect all variables (pre-array) and establish bounds
        bounds_v = VariableBoundVisitor()
        bounds_v.process(field_model_l, constraint_l, False)

        # TODO: need to handle inline constraints that impact arrays
        constraints_len = len(constraint_l)
        for fm in field_model_l:
            constraint_l.extend(
                ArrayConstraintBuilder.build(fm, bounds_v.bound_m))
            # Now, handle dist constraints
            DistConstraintBuilder.build(randstate, fm)

        for c in constraint_l:
            constraint_l.extend(
                ArrayConstraintBuilder.build(c, bounds_v.bound_m))
            # Now, handle dist constraints
            DistConstraintBuilder.build(randstate, c)

        # If we made changes during array remodeling,
        # re-run bounds checking on the updated model
#        if len(constraint_l) != constraints_len:
        bounds_v.process(field_model_l, constraint_l)

        if debug > 0:
            print("Final Model:")
            for fm in field_model_l:
                print("  " + ModelPrettyPrinter.print(fm))
            for c in constraint_l:
                print("  " + ModelPrettyPrinter.print(c, show_exp=True))

#        if lint > 0:
#            LintVisitor().lint(
#                field_model_l,
#                constraint_l)

        r = Randomizer(randstate,
                       solve_info=solve_info,
                       debug=debug,
                       lint=lint,
                       solve_fail_debug=solve_fail_debug)
        #        if Randomizer._rng is None:
        #            Randomizer._rng = random.Random(random.randrange(sys.maxsize))
        ri = RandInfoBuilder.build(field_model_l, constraint_l,
                                   Randomizer._rng)

        try:
            r.randomize(ri, bounds_v.bound_m)
        finally:
            # Rollback any constraints we've replaced for arrays
            if solve_info is not None:
                solve_info.totaltime = int(
                    (time.time() - solve_info.totaltime) * 1000)
                randomize_done(srcinfo, solve_info)
            for fm in field_model_l:
                ConstraintOverrideRollbackVisitor.rollback(fm)

        for fm in field_model_l:
            fm.post_randomize()