Implementation of reachability testing for RSC using SMT. Intitially tested version. No known issues.
This commit is contained in:
41
rctsys.py
41
rctsys.py
@@ -368,6 +368,14 @@ class ReactionSystemWithConcentrations(ReactionSystem):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_state_ids(self, state):
|
||||||
|
"""Returns entities of the given state without levels"""
|
||||||
|
return [e for e,c in state]
|
||||||
|
|
||||||
|
def has_non_zero_concentration(self, elem):
|
||||||
|
if elem[1] < 1:
|
||||||
|
raise RuntimeError("Unexpected concentration level in state: " + str(elem))
|
||||||
|
|
||||||
def add_reaction(self, R, I, P):
|
def add_reaction(self, R, I, P):
|
||||||
"""Adds a reaction"""
|
"""Adds a reaction"""
|
||||||
|
|
||||||
@@ -378,18 +386,21 @@ class ReactionSystemWithConcentrations(ReactionSystem):
|
|||||||
reactants = []
|
reactants = []
|
||||||
for r in R:
|
for r in R:
|
||||||
self.is_valid_entity_with_concentration(r)
|
self.is_valid_entity_with_concentration(r)
|
||||||
|
self.has_non_zero_concentration(r)
|
||||||
entity,level = r
|
entity,level = r
|
||||||
reactants.append((self.get_entity_id(entity),level))
|
reactants.append((self.get_entity_id(entity),level))
|
||||||
|
|
||||||
inhibitors = []
|
inhibitors = []
|
||||||
for i in I:
|
for i in I:
|
||||||
self.is_valid_entity_with_concentration(i)
|
self.is_valid_entity_with_concentration(i)
|
||||||
|
self.has_non_zero_concentration(i)
|
||||||
entity,level = i
|
entity,level = i
|
||||||
inhibitors.append((self.get_entity_id(entity),level))
|
inhibitors.append((self.get_entity_id(entity),level))
|
||||||
|
|
||||||
products = []
|
products = []
|
||||||
for p in P:
|
for p in P:
|
||||||
self.is_valid_entity_with_concentration(p)
|
self.is_valid_entity_with_concentration(p)
|
||||||
|
self.has_non_zero_concentration(p)
|
||||||
entity,level = p
|
entity,level = p
|
||||||
products.append((self.get_entity_id(entity),level))
|
products.append((self.get_entity_id(entity),level))
|
||||||
|
|
||||||
@@ -435,15 +446,20 @@ class ReactionSystemWithConcentrations(ReactionSystem):
|
|||||||
producible_entities = set()
|
producible_entities = set()
|
||||||
|
|
||||||
for reaction in self.reactions:
|
for reaction in self.reactions:
|
||||||
producible_entities = producible_entities.union(set(reaction[2]))
|
product_entities = [e for e,c in reaction[2]]
|
||||||
|
producible_entities = producible_entities.union(set(product_entities))
|
||||||
|
|
||||||
reactions_by_prod = {}
|
reactions_by_prod = {}
|
||||||
|
|
||||||
for prod_entity in producible_entities:
|
for p_e in producible_entities:
|
||||||
reactions_by_prod[prod_entity] = []
|
reactions_by_prod[p_e] = []
|
||||||
for reaction in self.reactions:
|
for r in self.reactions:
|
||||||
if prod_entity in reaction[2]:
|
product_entities = [e for e,c in r[2]]
|
||||||
reactions_by_prod[prod_entity].append([reaction[0],reaction[1]])
|
if p_e in product_entities:
|
||||||
|
reactants = r[0]
|
||||||
|
inhibitors = r[1]
|
||||||
|
products = [(e,c) for e,c in r[2] if e == p_e]
|
||||||
|
reactions_by_prod[p_e].append((reactants, inhibitors, products))
|
||||||
|
|
||||||
# save in cache
|
# save in cache
|
||||||
self.reactions_by_prod = reactions_by_prod
|
self.reactions_by_prod = reactions_by_prod
|
||||||
@@ -461,7 +477,16 @@ class ReactionSystemWithAutomaton(object):
|
|||||||
self.rs.show(soft)
|
self.rs.show(soft)
|
||||||
self.ca.show()
|
self.ca.show()
|
||||||
|
|
||||||
#
|
def is_with_concentrations(self):
|
||||||
|
if not isinstance(self.rs, ReactionSystemWithConcentrations):
|
||||||
|
return False
|
||||||
|
if not isinstance(self.ca, ContextAutomatonWithConcentrations):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def sanity_check(self):
|
||||||
|
pass
|
||||||
|
|
||||||
# class ReactionSystemWithConcentrationWithAutomaton(ReactionSystemWithAutomaton):
|
# class ReactionSystemWithConcentrationWithAutomaton(ReactionSystemWithAutomaton):
|
||||||
#
|
#
|
||||||
# def __init__(self, reaction_system, context_automaton):
|
# def __init__(self, reaction_system, context_automaton):
|
||||||
@@ -471,5 +496,3 @@ class ReactionSystemWithAutomaton(object):
|
|||||||
# def show(self, soft=False):
|
# def show(self, soft=False):
|
||||||
# self.rs.show(soft)
|
# self.rs.show(soft)
|
||||||
# self.ca.show()
|
# self.ca.show()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
rssmt.py
12
rssmt.py
@@ -6,10 +6,11 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from rctsys import ReactionSystem,ReactionSystemWithConcentrations,ContextAutomatonWithConcentrations
|
from rctsys import ReactionSystem,ReactionSystemWithConcentrations,ContextAutomatonWithConcentrations,ReactionSystemWithAutomaton
|
||||||
from smtchecker import SmtChecker
|
from smtchecker import SmtChecker
|
||||||
from smtcheckerpgrs import SmtCheckerPGRS
|
from smtcheckerpgrs import SmtCheckerPGRS
|
||||||
from smtcheckerdistribrs import SmtCheckerDistribRS
|
from smtcheckerdistribrs import SmtCheckerDistribRS
|
||||||
|
from smtcheckerrsc import SmtCheckerRSC
|
||||||
import sys
|
import sys
|
||||||
import rs_examples
|
import rs_examples
|
||||||
|
|
||||||
@@ -67,15 +68,22 @@ def main():
|
|||||||
r = ReactionSystemWithConcentrations()
|
r = ReactionSystemWithConcentrations()
|
||||||
r.add_bg_set_entities(["a","b","c","d"])
|
r.add_bg_set_entities(["a","b","c","d"])
|
||||||
|
|
||||||
r.add_reaction([("a",1)],[("b",2)],[("c",1)])
|
r.add_reaction([("c",1)],[("b",2)],[("c",1),("b",1)])
|
||||||
r.show()
|
r.show()
|
||||||
|
|
||||||
|
# print(r.get_reactions_by_product())
|
||||||
|
|
||||||
c = ContextAutomatonWithConcentrations(r)
|
c = ContextAutomatonWithConcentrations(r)
|
||||||
c.add_init_state("1")
|
c.add_init_state("1")
|
||||||
c.add_transition("1", [("c",1)], "1")
|
c.add_transition("1", [("c",1)], "1")
|
||||||
c.add_transition("1", [("d",2)], "1")
|
c.add_transition("1", [("d",2)], "1")
|
||||||
c.show()
|
c.show()
|
||||||
|
|
||||||
|
rc = ReactionSystemWithAutomaton(r,c)
|
||||||
|
|
||||||
|
smt = SmtCheckerRSC(rc)
|
||||||
|
|
||||||
|
smt.check_reachability([('c',1)],print_time=True,max_level=20)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
|
|||||||
164
smtcheckerrsc.py
164
smtcheckerrsc.py
@@ -69,47 +69,42 @@ class SmtCheckerRSC(object):
|
|||||||
|
|
||||||
return init_state_enc
|
return init_state_enc
|
||||||
|
|
||||||
def enc_enabledness(self, level, prod_entity):
|
def enc_produced_concentration(self, level, prod_entity):
|
||||||
"""Encodes the enabledness condition for a given level and a given entity"""
|
"""Encodes the produced concentrations for the given level and entity"""
|
||||||
|
|
||||||
rcts_for_prod_entity = self.rs.get_reactions_by_product()[prod_entity]
|
rcts_for_prod_entity = self.rs.get_reactions_by_product()[prod_entity]
|
||||||
|
|
||||||
if rcts_for_prod_entity == []:
|
if rcts_for_prod_entity == []:
|
||||||
return False
|
return simplify(self.v[level+1][prod_entity] == 0)
|
||||||
|
|
||||||
#
|
|
||||||
# TODO:
|
|
||||||
#
|
|
||||||
# czy potrzebujemy get_reactions_by_product?
|
|
||||||
# czy może wystarczy jak zakodujemy reakcje jako trójki?
|
|
||||||
#
|
|
||||||
# ... na pewno musi być informacja o produkowanym stężeniu
|
|
||||||
#
|
|
||||||
|
|
||||||
enc_rct_prod = False
|
enc_rct_prod = False
|
||||||
for reactants,inhibitors in rcts_for_prod_entity:
|
for reactants,inhibitors,products in rcts_for_prod_entity:
|
||||||
enc_reactants = True
|
enc_reactants = True
|
||||||
enc_inhibitors = True
|
enc_inhibitors = True
|
||||||
for reactant in reactants:
|
# enc_products -- below
|
||||||
enc_reactants = simplify(And(enc_reactants,
|
|
||||||
Or(self.v[level][reactant], self.v_ctx[level][reactant])))
|
|
||||||
for inhibitor in inhibitors:
|
|
||||||
enc_inhibitors = simplify(And(enc_inhibitors,
|
|
||||||
And(self.v[level][inhibitor], self.v_ctx[level][inhibitor])))
|
|
||||||
|
|
||||||
enc_rct_prod = simplify(Or(enc_rct_prod, And(enc_reactants, enc_inhibitors)))
|
for reactant,concentration in reactants:
|
||||||
|
enc_reactants = simplify(And(enc_reactants,
|
||||||
|
Or(self.v[level][reactant] >= concentration, self.v_ctx[level][reactant] >= concentration)))
|
||||||
|
for inhibitor,concentration in inhibitors:
|
||||||
|
enc_inhibitors = simplify(And(enc_inhibitors,
|
||||||
|
And(self.v[level][inhibitor] < concentration, self.v_ctx[level][inhibitor] < concentration)))
|
||||||
|
|
||||||
|
enc_products = self.v[level+1][products[0][0]] == products[0][1]
|
||||||
|
|
||||||
|
enc_rct_prod = simplify(Or(enc_rct_prod, And(enc_reactants, enc_inhibitors, enc_products)))
|
||||||
|
|
||||||
return enc_rct_prod
|
return enc_rct_prod
|
||||||
|
|
||||||
def enc_entity_production(self, level, prod_entity):
|
# def enc_entity_production(self, level, prod_entity):
|
||||||
"""Encodes the production of a given entity from a given level at level+1"""
|
# """Encodes the production of a given entity from a given level at level+1"""
|
||||||
|
#
|
||||||
enc_enab_cond = self.enc_enabledness(level, prod_entity)
|
# enc_enab_cond = self.enc_enabledness(level, prod_entity)
|
||||||
|
#
|
||||||
enc_ent_prod = Or(And(enc_enab_cond, self.v[level+1][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])))
|
# And(Not(enc_enab_cond), Not(self.v[level+1][prod_entity])))
|
||||||
|
#
|
||||||
return simplify(enc_ent_prod)
|
# return simplify(enc_ent_prod)
|
||||||
|
|
||||||
def enc_transition_relation(self, level):
|
def enc_transition_relation(self, level):
|
||||||
return simplify(And(self.enc_rs_trans(level), self.enc_automaton_trans(level)))
|
return simplify(And(self.enc_rs_trans(level), self.enc_automaton_trans(level)))
|
||||||
@@ -121,13 +116,15 @@ class SmtCheckerRSC(object):
|
|||||||
|
|
||||||
enc_trans = True
|
enc_trans = True
|
||||||
|
|
||||||
for prod_entity in self.rs.get_reactions_by_product():
|
reactions = self.rs.get_reactions_by_product()
|
||||||
|
|
||||||
|
for prod_entity in reactions:
|
||||||
unused_entities.remove(prod_entity)
|
unused_entities.remove(prod_entity)
|
||||||
|
|
||||||
enc_trans = simplify(And(enc_trans, self.enc_entity_production(level, prod_entity)))
|
enc_trans = simplify(And(enc_trans, self.enc_produced_concentration(level, prod_entity)))
|
||||||
|
|
||||||
for prod_entity in unused_entities:
|
for prod_entity in unused_entities:
|
||||||
enc_trans = simplify(And(enc_trans, Not(self.v[level+1][prod_entity])))
|
enc_trans = simplify(And(enc_trans, self.v[level+1][prod_entity] == 0))
|
||||||
|
|
||||||
return enc_trans
|
return enc_trans
|
||||||
|
|
||||||
@@ -141,15 +138,17 @@ class SmtCheckerRSC(object):
|
|||||||
dst_enc = self.ca_state[level+1] == dst
|
dst_enc = self.ca_state[level+1] == dst
|
||||||
|
|
||||||
all_ent = set(range(len(self.rs.background_set)))
|
all_ent = set(range(len(self.rs.background_set)))
|
||||||
incl_ctx = ctx
|
|
||||||
|
incl_ctx = set([e for e,c in ctx])
|
||||||
excl_ctx = all_ent - incl_ctx
|
excl_ctx = all_ent - incl_ctx
|
||||||
|
|
||||||
ctx_enc = True
|
ctx_enc = True
|
||||||
|
|
||||||
for c in incl_ctx:
|
for e,c in ctx:
|
||||||
ctx_enc = simplify(And(ctx_enc, self.v_ctx[level][c]))
|
ctx_enc = simplify(And(ctx_enc, self.v_ctx[level][e] == c))
|
||||||
for c in excl_ctx:
|
|
||||||
ctx_enc = simplify(And(ctx_enc, Not(self.v_ctx[level][c])))
|
for e in excl_ctx:
|
||||||
|
ctx_enc = simplify(And(ctx_enc, self.v_ctx[level][e] == 0))
|
||||||
|
|
||||||
cur_trans = simplify(And(src_enc, ctx_enc, dst_enc))
|
cur_trans = simplify(And(src_enc, ctx_enc, dst_enc))
|
||||||
enc_trans = simplify(Or(enc_trans, cur_trans))
|
enc_trans = simplify(Or(enc_trans, cur_trans))
|
||||||
@@ -202,19 +201,27 @@ class SmtCheckerRSC(object):
|
|||||||
|
|
||||||
print(" State: {", end=""),
|
print(" State: {", end=""),
|
||||||
for var_id in range(len(self.v[level])):
|
for var_id in range(len(self.v[level])):
|
||||||
if repr(m[self.v[level][var_id]]) == "True":
|
var_rep = repr(m[self.v[level][var_id]])
|
||||||
print(" " + self.rs.get_entity_name(var_id), end="")
|
if not var_rep.isdigit():
|
||||||
|
raise RuntimeError("unexpected: representation is not a positive integer")
|
||||||
|
if int(var_rep) > 0:
|
||||||
|
print(" " + self.rs.get_entity_name(var_id) + "=" + var_rep, end="")
|
||||||
|
# print(" " + repr(m[self.v[level][var_id]]), end="")
|
||||||
print(" }")
|
print(" }")
|
||||||
|
|
||||||
if level != max_level:
|
if level != max_level:
|
||||||
print(" Context set: ", end="")
|
print(" Context set: ", end="")
|
||||||
print("{", end="")
|
print("{", end="")
|
||||||
for var_id in range(len(self.v[level])):
|
for var_id in range(len(self.v[level])):
|
||||||
if repr(m[self.v_ctx[level][var_id]]) == "True":
|
var_rep = repr(m[self.v_ctx[level][var_id]])
|
||||||
print(" " + self.rs.get_entity_name(var_id), end="")
|
if not var_rep.isdigit():
|
||||||
|
raise RuntimeError("unexpected: representation is not a positive integer")
|
||||||
|
if int(var_rep) > 0:
|
||||||
|
print(" " + self.rs.get_entity_name(var_id) + "=" + var_rep, end="")
|
||||||
print(" }")
|
print(" }")
|
||||||
|
|
||||||
def check_reachability(self, state, print_witness=True, print_time=False):
|
def check_reachability(self, state, exact_state=False, print_witness=True,
|
||||||
|
print_time=False, print_mem=False, max_level=100):
|
||||||
"""Main testing function"""
|
"""Main testing function"""
|
||||||
|
|
||||||
if print_time:
|
if print_time:
|
||||||
@@ -224,33 +231,50 @@ class SmtCheckerRSC(object):
|
|||||||
self.solver.add(self.enc_init_state(0))
|
self.solver.add(self.enc_init_state(0))
|
||||||
current_level = 0
|
current_level = 0
|
||||||
|
|
||||||
print(self.enc_exact_state(current_level,state))
|
# print(self.enc_exact_state(current_level,state))
|
||||||
|
#
|
||||||
|
# print(self.enc_produced_concentration(current_level, 1))
|
||||||
|
|
||||||
# while True:
|
self.prepare_all_variables()
|
||||||
# print("\r[i] Level: " + str(current_level), end="")
|
print(And(self.enc_transition_relation(current_level), self.enc_init_state(0)))
|
||||||
# stdout.flush()
|
|
||||||
#
|
|
||||||
# self.prepare_all_variables()
|
while True:
|
||||||
#
|
self.prepare_all_variables()
|
||||||
# # reachability test:
|
|
||||||
# self.solver.push()
|
print("-----[ Working at level=" + str(current_level) + " ]-----")
|
||||||
# self.solver.add(self.enc_state(current_level,state))
|
stdout.flush()
|
||||||
#
|
|
||||||
# result = self.solver.check()
|
# reachability test:
|
||||||
# if result == sat:
|
print("[i] Adding the reachability test...")
|
||||||
# print("\n[+] SAT at level=" + str(current_level))
|
self.solver.push()
|
||||||
# if print_witness:
|
|
||||||
# self.decode_witness(current_level)
|
if exact_state:
|
||||||
# break
|
self.solver.add(self.enc_exact_state(current_level,state))
|
||||||
# else:
|
else:
|
||||||
# self.solver.pop()
|
self.solver.add(self.enc_min_state(current_level,state))
|
||||||
#
|
|
||||||
# self.solver.add(self.enc_transition_relation(current_level))
|
result = self.solver.check()
|
||||||
#
|
if result == sat:
|
||||||
# current_level += 1
|
print("\n[+] SAT at level=" + str(current_level))
|
||||||
#
|
if print_witness:
|
||||||
# if print_time:
|
self.decode_witness(current_level)
|
||||||
# stop = time()
|
break
|
||||||
# print()
|
else:
|
||||||
# print("[i] Time: " + repr(stop-start))
|
self.solver.pop()
|
||||||
|
|
||||||
|
print("[i] Unrolling the transition relation")
|
||||||
|
self.solver.add(self.enc_transition_relation(current_level))
|
||||||
|
|
||||||
|
print("-----[ level=" + str(current_level) + " done ]")
|
||||||
|
current_level += 1
|
||||||
|
|
||||||
|
if current_level > max_level:
|
||||||
|
print("Stopping at level=" + str(max_level))
|
||||||
|
break
|
||||||
|
|
||||||
|
if print_time:
|
||||||
|
stop = time()
|
||||||
|
print()
|
||||||
|
print("[i] Time: " + repr(stop-start))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user