示例#1
0
 def compute_reduced_core(self, i, j, val):
     """We compute the unsat core then remove any unnecessary terms."""
     if not (0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9):
         raise Exception(f'Index error: {i} {j} {val}')
     context = Context()
     self.assert_puzzle(context)
     self.assert_not_value(context, i, j, val)
     self.assert_trivial_rules(context)
     smt_stat = context.check_context_with_assumptions(
         None, self.duplicate_rules)
     # a valid puzzle should have a unique solution, so this should not happen, if it does we bail
     if smt_stat != Status.UNSAT:
         print(f'Error: {i} {j} {val} - not UNSAT: {Status.name(smt_stat)}')
         model = Model.from_context(context, 1)
         answer = self.puzzle_from_model(model)
         print(
             'Counter example (i.e. origonal puzzle does not have a unique solution):'
         )
         answer.pprint()
         model.dispose()
         context.dispose()
         return None
     core = context.get_unsat_core()
     filtered = core.copy()
     for term in core:
         filtered.remove(term)
         smt_stat = context.check_context_with_assumptions(None, filtered)
         if smt_stat != Status.UNSAT:
             filtered.append(term)
     context.dispose()
     print(
         f'Core: {i} {j} {val}   {len(filtered)} / {len(self.duplicate_rules)}'
     )
     return (i, j, val, filtered)
示例#2
0
 def compute_core(self, i, j, val):
     """We compute the unsat core of the duplicate_rules when asserting self.var(i, j) != val w.r.t the puzzle (val is assumed to be the unique solution)."""
     if not (0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9):
         raise Exception(f'Index error: {i} {j} {val}')
     context = Context()
     self.assert_puzzle(context)
     self.assert_not_value(context, i, j, val)
     self.assert_trivial_rules(context)
     smt_stat = context.check_context_with_assumptions(
         None, self.duplicate_rules)
     # a valid puzzle should have a unique solution, so this should not happen, if it does we bail
     if smt_stat != Status.UNSAT:
         print(f'Error: {i} {j} {val} - not UNSAT: {Status.name(smt_stat)}')
         model = Model.from_context(context, 1)
         answer = self.puzzle_from_model(model)
         print(
             'Counter example (i.e. origonal puzzle does not have a unique solution):'
         )
         answer.pprint()
         model.dispose()
         context.dispose()
         return None
     core = context.get_unsat_core()
     context.dispose()
     print(
         f'Core: {i} {j} {val}   {len(core)} / {len(self.duplicate_rules)}')
     return (i, j, val, core)
示例#3
0
 def filter_core(self, core):
     i, j, val, terms = core
     context = Context()
     self.assert_puzzle(context)
     self.assert_not_value(context, i, j, val)
     self.assert_trivial_rules(context)
     filtered = terms.copy()
     for term in terms:
         filtered.remove(term)
         smt_stat = context.check_context_with_assumptions(None, filtered)
         if smt_stat != Status.UNSAT:
             filtered.append(term)
     context.dispose()
     return (i, j, val, filtered)
示例#4
0
class Solver(object):

    def __init__(self, diagram, protocol, verbose):
        self.diagram = diagram
        self.protocol = protocol
        self.verbose = verbose
        self.maxTimeStamp = diagram.max_timestamp
        print('Maximum time = {0}'.format(self.maxTimeStamp))
        self.context = Context()
        # assert that (b i) = b_i and (pt x y) = pt_x_y
        yassert_formulas(self, YicesSignature.toYicesTerms())


    def addFacts(self):
        if not self.addModel():
            return False
        self.protocol.constrainVariablesAPI(self.context)
        if not self.addProtocol():
            return False
        return True

    def addModel(self):
        # The last argument indicates that the theory should be completed.
        # Anything not asserted is asserted to be False.
        assertions = self.diagram.toYicesTerms(point=None, completeMe=True)
        if assertions is None:
            assertions = []
        retval =  yassert_formulas(self, assertions)
        if not retval:
            sys.stderr.write('addModel() failed\n')
        return retval

    def addProtocol(self):
        assertions = self.protocol.toYicesTerms(None, None, self.maxTimeStamp)
        if assertions is None:
            assertions = []
        retval = yassert_formulas(self, assertions)
        if not retval:
            sys.stderr.write('addProtocol() failed\n')
        return retval


    def getProtocolIncrementally(self, level):
        return self.protocol.toYicesTermsIncrementally(level)

    def writeTheory(self, theory_file):
        sb = StringBuffer()
        SymbolTable.toYices(sb)
        self.diagram.toYices(sb, None, True)
        self.protocol.toYices(sb, self.maxTimeStamp, False)
        theory = str(sb)
        string2File(theory, theory_file, False)
        string2File("(check)\n(show-model)\n", theory_file, True)



    def consistency_check(self):
        assertions = self.diagram.toYicesTerms(point=None, completeMe=True)
        alen = len(assertions)
        if alen:
            smt_stat = self.context.check_context_with_assumptions(None, assertions)
            sys.stderr.write('context.check_context_with_assumptions returned {0}\n'.format(Status.name(smt_stat)))
            if smt_stat == Status.SAT:
                #sys.stderr.write('Calling unsat_core on a satisfiable theory is user error\n')
                retcode = 0
            elif smt_stat == Status.UNSAT:
                unsat_core = self.context.get_unsat_core()
                if not unsat_core:
                    sys.stderr.write('unsat_core is empty\n')
                else:
                    print("\nUnsat core:\n")
                    for term in unsat_core:
                        Terms.print_to_fd(1, term, 80, 100, 0)
                    print("\n")
            else:
                retcode = 1
            retcode = 0
        self._cleanUp()
        return retcode


    # a retcode of 0 means the problem is SAT
    # a retcode of 1 means the problem is UNSAT
    # a retcode of 2 means there was an error somewhere
    def solve(self, theory_file):
        retcode = 2
        if not self.addFacts():
            return retcode
        if theory_file is not None:
            self.writeTheory(theory_file)
        smt_stat = self.context.check_context()
        if smt_stat != Status.SAT:
            print('No solution: smt_stat = {0}\n'.format(smt_stat))
            retcode = 1
        else:
            print('\nSatisfiable.')
            #get the model
            model = Model.from_context(self.context, 1)
            self.print_solution(model)
            model.dispose()
            retcode = 0
        self._cleanUp()
        return retcode




    def incrementally_solve(self):
        retcode = 2
        if not self.addModel():
            return retcode
        self.protocol.constrainVariablesAPI(self.context)
        if self.context.status() == Status.UNSAT:
            print('The model appears to be UNSAT: smt_stat = {}\n'.format(self.context.status()))
            print('For more information use the --consistency option.\n')
            return retcode
        # The meaning of the levels:
        #
        # 0.  Just the events
        # 1.  The events plus the distinctness of each timeline:  distinct(varlist) for timeline(bot, varlist) in facts.
        # 2.  The events plus the ascendingness of each timeline
        for level in range(0, 4):
            #print('context status: ', Status.name(self.context.status()))
            self.context.push()
            assertions = self.getProtocolIncrementally(level)
            alen = len(assertions)
            if alen:
                smt_stat = self.context.check_context_with_assumptions(None, assertions)
                sys.stderr.write('Level {0}: context.check_context_with_assumptions returned {1}\n'.format(level, Status.name(smt_stat)))
                if smt_stat == Status.SAT:
                    #get the model
                    model = Model.from_context(self.context, 1)
                    print('Level {0} has a solution: smt_stat = {1}\n'.format(level, smt_stat))
                    self.print_solution(model)
                    model.dispose()
                elif smt_stat == Status.UNSAT:
                    unsat_core = self.context.get_unsat_core()
                    if not unsat_core:
                        sys.stderr.write('unsat_core is empty\n')
                    else:
                        print('Level {0} unsat core is:\n'.format(level))
                        for term in unsat_core:
                            Terms.print_to_fd(1, term, 80, 100, 0)
                        print('')
                    return 0
                else:
                    sys.stderr.write('context.check_context_with_assumptions returned {0}\n'.format(Status.name(smt_stat)))
            self.context.pop()
        self._cleanUp()
        return 0


    def exhaust(self):
        result = 0
        self.context.push()
        if not self.addFacts():
            print('Bummer')
            return 2
        # BD says: this is complete but crude
        # another way would be to use 'yices_assert_blocking_clause'
        while  self.context.check_context() == Status.SAT:
            model = Model.from_context(self.context, 1)
            self.print_solution(model, 'Model #{0}:'.format(result))
            diagram = YicesSignature.model2term(model)
            self.context.assert_formula(Terms.ynot(diagram))
            model.dispose()
            result += 1
            if result == Configuration.aleph_nought:
                break
        self.context.pop()
        print("I found {1} distinct model{2} (using an upper limit of {0})".format(Configuration.aleph_nought, result, "" if result == 1 else "s"))
        return result



    def unsat_core(self):
        retcode = 2
        if not self.addModel():
            sys.stderr.write('addModel failed\n')
            return retcode
        self.protocol.constrainVariablesAPI(self.context)
        smt_stat = self.context.check_context()
        #sys.stderr.write('context.check_context returned {0}\n'.format(Status.name(smt_stat)))
        if smt_stat == Status.UNSAT:
            sys.stderr.write('The model is UNSAT: something is rotten in the State\n')
            retcode = 1
            return retcode
        assertions = self.protocol.toYicesTerms()
        alen = len(assertions)
        if alen:
            smt_stat = self.context.check_context_with_assumptions(None, assertions)
            sys.stderr.write('context.check_context_with_assumptions returned {0}\n'.format(Status.name(smt_stat)))
            if smt_stat == Status.SAT:
                sys.stderr.write('Calling unsat_core on a satisfiable theory is user error\n')
                retcode = 1
            elif smt_stat == Status.UNSAT:
                unsat_core = self.context.get_unsat_core()
                if not unsat_core:
                    sys.stderr.write('unsat_core is empty\n')
                else:
                    for term in unsat_core:
                        Terms.print_to_fd(1, term, 80, 100, 0)
            else:
                retcode = 1
            retcode = 0
        self._cleanUp()
        return retcode


    def missing(self):
        retcode = 2
        if not self.addModel():
            sys.stderr.write('addModel failed\n')
            return retcode
        self.protocol.constrainVariablesAPI(self.context)
        timelines = self.protocol.timelines
        tlen = len(timelines)
        interpretation = Configuration.timeline_interpretation
        tint_text = Configuration.INTERPRETATIONS[interpretation]
        print('There are {0} timelines ({1})\n'.format(tlen, tint_text))
        if not timelines:
            return retcode
        for timeline in timelines:
            formula = timeline.toYicesTerm(interpretation)
            yassert_formula(self, formula)
        smt_stat = self.context.check_context()
        if smt_stat == Status.UNSAT:
            sys.stderr.write('The model is UNSAT: something is rotten in the State\n')
            retcode = 1
            return retcode
        for timeline in timelines:
            satisfiable = True
            yevents = [ ]
            for eindex, timevar in enumerate(timeline.varlist):
                events = self.protocol.event_map[timevar]
                for e in events:
                    yevents.append(e.yices_term)
                smt_stat = self.context.check_context_with_assumptions(None, yevents)
                if smt_stat == Status.UNSAT:
                    satisfiable = False
                    print('UNSAT at level {0}, i.e. timestamp {1}'.format(eindex, timevar))
                    unsat_core = self.context.get_unsat_core()
                    print("\nUnsat core :\n")
                    for term in unsat_core:
                        Terms.print_to_fd(1, term, 80, 100, 0)
                    print("\n")
                    break
                elif self.verbose and smt_stat == Status.SAT:
                    print('SAT at level {0}, i.e. timestamp {1}'.format(eindex, timevar))
                    model = Model.from_context(self.context, 1)
                    self.print_solution(model, header=None, frees=timeline.fv)
                    model.dispose()
            if satisfiable:
                print('Timeline of {0} OK ({1}):\n'.format(timeline.term, tint_text))  #print a model
                model = Model.from_context(self.context, 1)
                self.print_solution(model, header=None, frees=timeline.fv)
                model.dispose()
        retcode = 0
        self._cleanUp()
        return retcode



    def print_solution(self, model, header=None, frees=None):

        def print_int(v):
            ytvar = v.yices_term
            ytval = model.get_value(ytvar)
            print('\t{0} is {1}'.format(v.name, ytval))


        if header is not None:
            print(header)

        if frees is None:
            nv = YicesSignature.get_vars(SymbolTable.NAT)
            for nvar in nv:
                print_int(nvar)
            tv = YicesSignature.get_vars(SymbolTable.TIME)
            for tvar in tv:
                if tvar.name == SymbolTable.MAXTIME:
                    continue
                print_int(tvar)
            pv = YicesSignature.get_vars(SymbolTable.PT2)
            for ptvar in pv:
                if ptvar.name == SymbolTable.NOLOC:
                    continue
                ytvar = ptvar.yices_term
                ytval = model.get_value(ytvar)
                val = YicesSignature.pt2_invmap[ytval]
                print('\t{0} is {1}'.format(ptvar.name, val))
        else:
            frees = list(frees)
            frees.sort()
            for var in frees:
                ytvar = var.yices_term
                ytval = model.get_value(ytvar)
                if var.vartype.name == 'Pt2':
                    ytval = YicesSignature.pt2_invmap[ytval]
                print('\t{0} is {1}'.format(var.name, ytval))


    def frontier(self):
        retcode = 2
        if not self.addModel():
            return retcode
        self.protocol.constrainVariablesAPI(self.context)
        searchSpace = Frontier(self.protocol)
        interpretation = Configuration.timeline_interpretation
        tint_text = Configuration.INTERPRETATIONS[interpretation]
        print('The timeline interpretation is: {0}\n'.format(tint_text))
        while not searchSpace.finished():
            point = searchSpace.nextElement()
            if point is None:
                break
            if self.verbose:
                print(point)
            assertions = self.protocol.toYicesTerms(point)
            alen = len(assertions)
            if alen:
                smt_stat = self.context.check_context_with_assumptions(None, assertions)
                if smt_stat == Status.SAT:
                    if self.verbose:
                        print('SAT')
                        #get the model
                        model = Model.from_context(self.context, 1)
                        self.print_solution(model, header=None, frees=self.protocol.FV(point))
                        model.dispose()
                elif smt_stat == Status.UNSAT:
                    searchSpace.addUnsat(point)
                    if self.verbose:
                        print('UNSAT')
                        unsat_core = self.context.get_unsat_core()
                        print("\nUnsat core:\n")
                        for term in unsat_core:
                            Terms.print_to_fd(1, term, 80, 100, 0)
                        print("\n")
            else:
                sys.stderr.write('context.check_context_with_assumptions returned {0}\n', smt_stat)
        print('Frontier: {0}'.format(searchSpace.frontier))
        self._cleanUp()
        return retcode


    def entire(self):
        retcode = 2
        if not self.addModel():
            return retcode
        self.protocol.constrainVariablesAPI(self.context)
        searchSpace = Frontier(self.protocol)
        tower = list(searchSpace.tower())
        tower.sort()
        for point in tower:
            print(point)
            assertions = self.protocol.toYicesTerms(point)
            alen = len(assertions)
            if alen:
                smt_stat = self.context.check_context_with_assumptions(None, assertions)
                if smt_stat == Status.SAT:
                    print('SAT')
                    #get the model
                    model = Model.from_context(self.context, 1)
                    self.print_solution(model, header=None, frees=self.protocol.FV(point))
                    model.dispose()
                elif smt_stat == Status.UNSAT:
                    print('UNSAT')
                    unsat_core = self.context.get_unsat_core()
                    print("\nUnsat core:\n")
                    for term in unsat_core:
                        Terms.print_to_fd(1, term, 80, 100, 0)
                    print("\n")
                else:
                    sys.stderr.write('context.check_context_with_assumptions returned {0}\n', smt_stat)
        self._cleanUp()
        return retcode




    def _cleanUp(self):
        self.context.dispose()
        Yices.exit()