mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-30 19:22:28 +00:00 
			
		
		
		
	add example for MSS enumeration
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									b66d457b19
								
							
						
					
					
						commit
						f786ab15fb
					
				
					 1 changed files with 150 additions and 0 deletions
				
			
		
							
								
								
									
										150
									
								
								examples/python/mus/mss.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								examples/python/mus/mss.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | ||||||
|  | ############################################ | ||||||
|  | # Copyright (c) 2016 Microsoft Corporation | ||||||
|  | #  | ||||||
|  | # MSS enumeration based on maximal resolution. | ||||||
|  | # | ||||||
|  | # Author: Nikolaj Bjorner (nbjorner) | ||||||
|  | ############################################ | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | The following is a procedure for enumerating maximal satisfying subsets. | ||||||
|  | It uses maximal resolution to eliminate cores from the state space. | ||||||
|  | Whenever the hard constraints are satisfiable, it finds a model that | ||||||
|  | satisfies the maximal number of soft constraints.  | ||||||
|  | During this process it collects the set of cores that are encountered. | ||||||
|  | It then reduces the set of soft constraints using max-resolution in | ||||||
|  | the style of [Narodytska & Bacchus, AAAI'14]. In other words,  | ||||||
|  | let F1, ..., F_k be a core among the soft constraints F1,...,F_n  | ||||||
|  | Replace F1,.., F_k by  | ||||||
|  |           F1 or F2, F3 or (F2 & F1), F4 or (F3 & (F2 & F1)), ...,  | ||||||
|  |           F_k or (F_{k-1} & (...)) | ||||||
|  | Optionally, add the core ~F1 or ... or ~F_k to F | ||||||
|  | The current model M satisfies the new set F, F1,...,F_{n-1} if the core is minimal. | ||||||
|  | Whenever we modify the soft constraints by the core reduction any assignment | ||||||
|  | to the reduced set satisfies a k-1 of the original soft constraints. | ||||||
|  |      | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | from z3 import * | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     x, y = Reals('x y') | ||||||
|  |     soft_constraints = [x > 2, x < 1, x < 0, Or(x + y > 0, y < 0), Or(y >= 0, x >= 0), Or(y < 0, x < 0), Or(y > 0, x < 0)] | ||||||
|  |     hard_constraints = BoolVal(True) | ||||||
|  |     solver = MSSSolver(hard_constraints, soft_constraints) | ||||||
|  |     for lits in enumerate_sets(solver): | ||||||
|  |         print("%s" % lits) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def enumerate_sets(solver): | ||||||
|  |     while True: | ||||||
|  |         if sat == solver.s.check(): | ||||||
|  |            MSS = solver.grow() | ||||||
|  |            yield MSS | ||||||
|  |         else: | ||||||
|  |            break | ||||||
|  | 
 | ||||||
|  | class CompareSetSize(): | ||||||
|  |    def __call__(self, s1, s2): | ||||||
|  |        return len(s1) < len(s2)  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MSSSolver: | ||||||
|  |    s = Solver() | ||||||
|  |    varcache = {} | ||||||
|  |    idcache = {} | ||||||
|  | 
 | ||||||
|  |    def __init__(self, hard, soft): | ||||||
|  |        self.n = len(soft) | ||||||
|  |        self.soft = soft | ||||||
|  |        self.s.add(hard)        | ||||||
|  |        self.soft_vars = set([self.c_var(i) for i in range(self.n)]) | ||||||
|  |        self.orig_soft_vars = set([self.c_var(i) for i in range(self.n)]) | ||||||
|  |        self.s.add([(self.c_var(i) == soft[i]) for i in range(self.n)]) | ||||||
|  | 
 | ||||||
|  |    def c_var(self, i): | ||||||
|  |        if i not in self.varcache: | ||||||
|  |           v = Bool(str(self.soft[abs(i)])) | ||||||
|  |           self.idcache[v] = abs(i) | ||||||
|  |           if i >= 0: | ||||||
|  |              self.varcache[i] = v | ||||||
|  |           else: | ||||||
|  |              self.varcache[i] = Not(v) | ||||||
|  |        return self.varcache[i] | ||||||
|  | 
 | ||||||
|  |    def update_unknown(self): | ||||||
|  |        self.model = self.s.model() | ||||||
|  |        new_unknown = set([]) | ||||||
|  |        for x in self.unknown: | ||||||
|  |            if is_true(self.model[x]): | ||||||
|  |               self.mss.append(x) | ||||||
|  |            else: | ||||||
|  |               new_unknown.add(x) | ||||||
|  |        self.unknown = new_unknown | ||||||
|  |        | ||||||
|  |    def relax_core(self, core): | ||||||
|  |        assert(core <= self.soft_vars) | ||||||
|  |        prev = BoolVal(True) | ||||||
|  |        core_list = [x for x in core] | ||||||
|  |        self.soft_vars -= core | ||||||
|  |        # replace x0, x1, x2, .. by | ||||||
|  |        # Or(x1, x0), Or(x2, And(x1, x0)), Or(x3, And(x2, And(x1, x0))), ... | ||||||
|  |        for i in range(len(core_list)-1): | ||||||
|  |            x = core_list[i] | ||||||
|  |            y = core_list[i+1] | ||||||
|  |            prevf = And(x, prev) | ||||||
|  |            prev = Bool("%s" % prevf) | ||||||
|  |            self.s.add(prev == prevf) | ||||||
|  |            zf = Or(prev, y) | ||||||
|  |            z = Bool("%s" % zf) | ||||||
|  |            self.s.add(z == zf) | ||||||
|  |            self.soft_vars.add(z) | ||||||
|  | 
 | ||||||
|  |    def resolve_core(self, core): | ||||||
|  |        new_core = set([]) | ||||||
|  |        for x in core: | ||||||
|  |            if x in self.mcs_map: | ||||||
|  |               new_core |= self.mcs_map[x] | ||||||
|  |            else: | ||||||
|  |               new_core.add(x) | ||||||
|  |        return new_core | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |    def grow(self): | ||||||
|  |        self.mss = [] | ||||||
|  |        self.mcs = [] | ||||||
|  |        self.nmcs = [] | ||||||
|  |        self.mcs_map = {} | ||||||
|  |        self.unknown = self.soft_vars | ||||||
|  |        self.update_unknown() | ||||||
|  |        cores = [] | ||||||
|  |        while len(self.unknown) > 0: | ||||||
|  |            x = self.unknown.pop() | ||||||
|  |            is_sat = self.s.check(self.mss + [x] + self.nmcs) | ||||||
|  |            if is_sat == sat: | ||||||
|  |               self.mss.append(x) | ||||||
|  |               self.update_unknown() | ||||||
|  |            elif is_sat == unsat: | ||||||
|  |               core = self.s.unsat_core() | ||||||
|  |               core = self.resolve_core(core) | ||||||
|  |               self.mcs_map[x] = {y for y in core if not eq(x,y)} | ||||||
|  |               self.mcs.append(x) | ||||||
|  |               self.nmcs.append(Not(x))  | ||||||
|  |               cores += [core]               | ||||||
|  |            else: | ||||||
|  |               print("solver returned %s" % is_sat) | ||||||
|  |               exit() | ||||||
|  |        mss = [x for x in self.orig_soft_vars if is_true(self.model[x])] | ||||||
|  |        mcs = [x for x in self.orig_soft_vars if not is_true(self.model[x])] | ||||||
|  |        self.s.add(Or(mcs)) | ||||||
|  |        core_literals = set([]) | ||||||
|  |        cores.sort(CompareSetSize())  | ||||||
|  |        for core in cores: | ||||||
|  |            if len(core & core_literals) == 0: | ||||||
|  |               self.relax_core(core) | ||||||
|  |               core_literals |= core | ||||||
|  |        return mss | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | main() | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue