""" SMT-based Model Checking Module for RS """ from z3 import * from time import time from sys import stdout class SmtChecker(object): def __init__(self, rs): ############################################################## # Encoded RS ############################################################## rs.sanity_check() self.reaction_system = rs ############################################################## # SMT variables ############################################################## self.v = [] self.v_init = [] #self.vSucc = [] #self.vSuccInit = [] self.v_ctx = [] self.next_level_to_encode = 0 ############################################################## # SMT solver instance ############################################################## self.solver = Solver() #def smtVar(self, level, entityID, primed=False): # return "?" def prepare_context_variables(self): """Encodes all the context variables""" level = self.next_level_to_encode variables = [] for entity in self.reaction_system.background_set: variables.append(Bool("C"+str(level)+"_"+entity)) self.v_ctx.append(variables) def prepare_state_variables(self): """Encodes all the state variables (including successors)""" level = self.next_level_to_encode variables = [] #variablesSucc = [] for entity in self.reaction_system.background_set: variables.append(Bool("L"+str(level)+"_"+entity)) #variablesSucc.append(Bool("R"+str(level)+"_"+entity)) self.v.append(variables) #self.vSucc.append(variablesSucc) self.v_init.append(Bool("L"+str(level)+"_Init")) #self.vSuccInit.append(Bool("R"+str(level)+"_Init")) def prepare_all_variables(self): """Encodes all the variables""" self.prepare_state_variables() self.prepare_context_variables() self.next_level_to_encode += 1 def enc_init_state(self, level): """Encodes the initial state at the given level""" init_state_enc = self.v_init[level] for v in self.v[level]: init_state_enc = simplify(And(init_state_enc, Not(v))) return init_state_enc def enc_init_contexts(self, level): """Encodes the initial contexts set at the given level""" init_contexts_set_enc = False # Or for ctx in self.reaction_system.init_contexts: single_ctx_enc = True # And not_ctx_entities = list(range(0, len(self.reaction_system.background_set))) for entity in ctx: single_ctx_enc = simplify(And(single_ctx_enc, self.v_ctx[level][entity])) not_ctx_entities.remove(entity) for entity in not_ctx_entities: single_ctx_enc = simplify(And(single_ctx_enc, Not(self.v_ctx[level][entity]))) init_contexts_set_enc = simplify(Or(init_contexts_set_enc, single_ctx_enc)) #print("initContextSetEnc: " + repr(initContextsSetEnc)) return init_contexts_set_enc def enc_not_allowed_contexts(self, level): """Encodes all the context entities that are not allowed in the context sets""" bg_set = set(range(0,len(self.reaction_system.background_set))) ctx_ent_set = set(self.reaction_system.context_entities) not_ctx_ent_set = bg_set.difference(ctx_ent_set) enc = True for entity in not_ctx_ent_set: enc = simplify(And(enc, Not(self.v_ctx[level][entity]))) return enc def enc_enabledness(self, level, prod_entity): """Encodes the enabledness condition for a given level and a given entity""" rcts_for_prod_entity = self.reaction_system.get_reactions_by_product()[prod_entity] if rcts_for_prod_entity == []: return False #encInitEnab = simplify(Or(And(self.vInit[level], self.encInitContexts(level)), # And(Not(self.vInit[level]), self.encNotAllowedContexts(level)))) enc_rct_prod = False for ri_pair in rcts_for_prod_entity: # reactants-inhibitors pair enc_reactants = True enc_inhibitors = True for reactant in ri_pair[0]: enc_reactants = simplify(And(enc_reactants, Or(self.v[level][reactant], self.v_ctx[level][reactant]))) for inhibitor in ri_pair[1]: enc_inhibitors = simplify(And(enc_inhibitors, Not(Or(self.v[level][inhibitor], self.v_ctx[level][inhibitor])))) enc_rct_prod = simplify(Or(enc_rct_prod, And(enc_reactants, enc_inhibitors))) #print("encEnabledness(" + repr(prodEntity) + "): " + repr(simplify(And(encInitEnab, encRctProd)))) #return simplify(And(encInitEnab, encRctProd)) return enc_rct_prod def enc_entity_production(self, level, prod_entity): """Encodes the production of a given entity from a given level at level+1""" enc_enab_cond = self.enc_enabledness(level, prod_entity) enc_ent_prod = Or(And(enc_enab_cond, self.v[level+1][prod_entity]), And(Not(enc_enab_cond), Not(self.v[level+1][prod_entity]))) return enc_ent_prod def enc_transition_relation(self, level): """Encodes the transition relation""" unused_entities = list(range(0,len(self.reaction_system.background_set))) enc_trans = True for prod_entity in self.reaction_system.get_reactions_by_product(): unused_entities.remove(prod_entity) enc_trans = simplify(And(enc_trans, self.enc_entity_production(level, prod_entity))) enc_trans = simplify(And(enc_trans, Not(self.v_init[level+1]))) for prod_entity in unused_entities: enc_trans = simplify(And(enc_trans, Not(self.v[level+1][prod_entity]))) if level == 0: enc_init_enab = self.enc_init_contexts(level) else: # level > 0: enc_init_enab = self.enc_not_allowed_contexts(level) #encInitEnab = simplify(Or(And(self.vInit[level], self.encInitContexts(level)), # And(Not(self.vInit[level]), self.encNotAllowedContexts(level)))) enc_trans = simplify(And(enc_trans, enc_init_enab)) return enc_trans def enc_state(self, level, state): """Encodes the state at the given level""" enc = Not(self.v_init[level]) state_ids = self.reaction_system.get_state_ids(state) for entity in state_ids: enc = And(enc, self.v[level][entity]) not_in_state = set(range(0, len(self.reaction_system.background_set))) not_in_state = not_in_state.difference(set(state_ids)) for entity in not_in_state: enc = And(enc, Not(self.v[level][entity])) return enc def decode_witness(self, max_level): m = self.solver.model() for level in range(0,max_level+1): print("\n[Level=" + repr(level) + "]") if repr(m[self.v_init[level]]) == "True": print("** Initial state") print("State:\n{"), for var_id in range(0, len(self.v[level])): if repr(m[self.v[level][var_id]]) == "True": print("\t" + self.reaction_system.get_entity_name(var_id)), print("}") if level != max_level: print("Context set:"), print("{"), for var_id in range(0, len(self.v[level])): if repr(m[self.v_ctx[level][var_id]]) == "True": print("\t" + self.reaction_system.get_entity_name(var_id)), print("}") def check_reachability(self, state, print_witness=True, print_time=False): """Main testing function""" if print_time: start = time() self.prepare_all_variables() self.solver.add(self.enc_init_state(0)) current_level = 0 while True: #print("Level: " + str(current_level)) print("\rLevel: " + str(current_level)), stdout.flush() self.prepare_all_variables() # reachability test: self.solver.push() self.solver.add(self.enc_state(current_level,state)) result = self.solver.check() print(result) if result == sat: print("\nSAT") if print_witness: self.decode_witness(current_level) break else: self.solver.pop() self.solver.add(self.enc_transition_relation(current_level)) current_level += 1 if print_time: stop = time() print("Time: " + repr(stop-start)) # # class SmtCheckerPGRS(object): # # def __init__(self, rs): # # ############################################################## # # Encoded RS # ############################################################## # rs.sanity_check() # self.reaction_system = rs # # ############################################################## # # SMT variables # ############################################################## # self.v = [] # self.v_init = [] # # #self.vSucc = [] # #self.vSuccInit = [] # # self.v_ctx = [] # # self.next_level_to_encode = 0 # # ############################################################## # # SMT solver instance # ############################################################## # self.solver = Solver() # # #def smtVar(self, level, entityID, primed=False): # # return "?" # # def prepare_context_variables(self): # """Encodes all the context variables""" # # level = self.next_level_to_encode # # variables = [] # for entity in self.reaction_system.background_set: # variables.append(Bool("C"+str(level)+"_"+entity)) # # self.v_ctx.append(variables) # # def prepare_state_variables(self): # """Encodes all the state variables (including successors)""" # # level = self.next_level_to_encode # # variables = [] # #variablesSucc = [] # for entity in self.reaction_system.background_set: # variables.append(Bool("L"+str(level)+"_"+entity)) # #variablesSucc.append(Bool("R"+str(level)+"_"+entity)) # # self.v.append(variables) # #self.vSucc.append(variablesSucc) # # self.v_init.append(Bool("L"+str(level)+"_Init")) # #self.vSuccInit.append(Bool("R"+str(level)+"_Init")) # # def prepare_all_variables(self): # """Encodes all the variables""" # # self.prepare_state_variables() # self.prepare_context_variables() # self.next_level_to_encode += 1 # # def enc_init_state(self, level): # """Encodes the initial state at the given level""" # # init_state_enc = self.v_init[level] # # for v in self.v[level]: # init_state_enc = simplify(And(init_state_enc, Not(v))) # # return init_state_enc # # def enc_init_contexts(self, level): # """Encodes the initial contexts set at the given level""" # # init_contexts_set_enc = False # Or # # for ctx in self.reaction_system.init_contexts: # single_ctx_enc = True # And # # not_ctx_entities = list(range(0, len(self.reaction_system.background_set))) # for entity in ctx: # single_ctx_enc = simplify(And(single_ctx_enc, self.v_ctx[level][entity])) # not_ctx_entities.remove(entity) # # for entity in not_ctx_entities: # single_ctx_enc = simplify(And(single_ctx_enc, Not(self.v_ctx[level][entity]))) # # init_contexts_set_enc = simplify(Or(init_contexts_set_enc, single_ctx_enc)) # # #print("initContextSetEnc: " + repr(initContextsSetEnc)) # return init_contexts_set_enc # # def enc_not_allowed_contexts(self, level): # """Encodes all the context entities that are not allowed in the context sets""" # # bg_set = set(range(0,len(self.reaction_system.background_set))) # ctx_ent_set = set(self.reaction_system.context_entities) # # not_ctx_ent_set = bg_set.difference(ctx_ent_set) # # enc = True # for entity in not_ctx_ent_set: # enc = simplify(And(enc, Not(self.v_ctx[level][entity]))) # # return enc # # def enc_enabledness(self, level, prod_entity): # """Encodes the enabledness condition for a given level and a given entity""" # # rcts_for_prod_entity = self.reaction_system.get_reactions_by_product()[prod_entity] # # if rcts_for_prod_entity == []: # return False # # #encInitEnab = simplify(Or(And(self.vInit[level], self.encInitContexts(level)), # # And(Not(self.vInit[level]), self.encNotAllowedContexts(level)))) # # enc_rct_prod = False # for ri_pair in rcts_for_prod_entity: # reactants-inhibitors pair # enc_reactants = True # enc_inhibitors = True # for reactant in ri_pair[0]: # enc_reactants = simplify(And(enc_reactants, # Or(self.v[level][reactant], self.v_ctx[level][reactant]))) # for inhibitor in ri_pair[1]: # enc_inhibitors = simplify(And(enc_inhibitors, # Not(Or(self.v[level][inhibitor], self.v_ctx[level][inhibitor])))) # # enc_rct_prod = simplify(Or(enc_rct_prod, And(enc_reactants, enc_inhibitors))) # # #print("encEnabledness(" + repr(prodEntity) + "): " + repr(simplify(And(encInitEnab, encRctProd)))) # #return simplify(And(encInitEnab, encRctProd)) # return enc_rct_prod # # def enc_entity_production(self, level, prod_entity): # """Encodes the production of a given entity from a given level at level+1""" # # enc_enab_cond = self.enc_enabledness(level, prod_entity) # # enc_ent_prod = Or(And(enc_enab_cond, self.v[level+1][prod_entity]), # And(Not(enc_enab_cond), Not(self.v[level+1][prod_entity]))) # # return enc_ent_prod # # def enc_transition_relation(self, level): # """Encodes the transition relation""" # # unused_entities = list(range(0,len(self.reaction_system.background_set))) # # enc_trans = True # # for prod_entity in self.reaction_system.get_reactions_by_product(): # unused_entities.remove(prod_entity) # # enc_trans = simplify(And(enc_trans, self.enc_entity_production(level, prod_entity))) # # enc_trans = simplify(And(enc_trans, Not(self.v_init[level+1]))) # # for prod_entity in unused_entities: # enc_trans = simplify(And(enc_trans, Not(self.v[level+1][prod_entity]))) # # if level == 0: # enc_init_enab = self.enc_init_contexts(level) # else: # level > 0: # enc_init_enab = self.enc_not_allowed_contexts(level) # # #encInitEnab = simplify(Or(And(self.vInit[level], self.encInitContexts(level)), # # And(Not(self.vInit[level]), self.encNotAllowedContexts(level)))) # # enc_trans = simplify(And(enc_trans, enc_init_enab)) # # return enc_trans # # def enc_state(self, level, state): # """Encodes the state at the given level""" # # enc = Not(self.v_init[level]) # # state_ids = self.reaction_system.get_state_ids(state) # # for entity in state_ids: # enc = And(enc, self.v[level][entity]) # # not_in_state = set(range(0, len(self.reaction_system.background_set))) # not_in_state = not_in_state.difference(set(state_ids)) # # for entity in not_in_state: # enc = And(enc, Not(self.v[level][entity])) # # return enc # # def decode_witness(self, max_level): # # m = self.solver.model() # # for level in range(0,max_level+1): # # print("\n[Level=" + repr(level) + "]") # # if repr(m[self.v_init[level]]) == "True": # print("** Initial state") # # print("State:\n{"), # for var_id in range(0, len(self.v[level])): # if repr(m[self.v[level][var_id]]) == "True": # print("\t" + self.reaction_system.get_entity_name(var_id)), # print("}") # # if level != max_level: # print("Context set:"), # print("{"), # for var_id in range(0, len(self.v[level])): # if repr(m[self.v_ctx[level][var_id]]) == "True": # print("\t" + self.reaction_system.get_entity_name(var_id)), # print("}") # # # def check_reachability(self, state, print_witness=True, print_time=False): # """Main testing function""" # # if print_time: # start = time() # # self.prepare_all_variables() # self.solver.add(self.enc_init_state(0)) # current_level = 0 # # while True: # #print("Level: " + str(current_level)) # print("\rLevel: " + str(current_level)), # stdout.flush() # # self.prepare_all_variables() # # # reachability test: # self.solver.push() # self.solver.add(self.enc_state(current_level,state)) # # result = self.solver.check() # print(result) # if result == sat: # print("\nSAT") # if print_witness: # self.decode_witness(current_level) # break # else: # self.solver.pop() # # self.solver.add(self.enc_transition_relation(current_level)) # # current_level += 1 # # if print_time: # stop = time() # print("Time: " + repr(stop-start))