def generate_smt_from_cn_and_log(cn, log, var_info, min_unused_bindings=None, max_global_obligations=0): """Main function to generate a CVC file (to stdout) that represents the C-net simulation problem. The file can then be fed to the STP solver. Returns the number of potentially unused bindings in the C-net. [cn] C-net to simulate. [log] log to simulate. Must fit inside [cn] [min_unused_bindings] Minimum number of required unused bindings. If 0, then compute the number of mandatory bindings and the number of potentially unused bindings. Use this latter value halfed as required number of minimum unused bindings. Use None if no minimum number of bindings is required. [max_global_obligations] If > 0, restricts the maximum number of generated obligations during the simulation of the log. """ #print the variables traces = log.get_uniq_cases() activity_positions = log.get_activity_positions() variables = var_info.variables obligation_dict = var_info.obligation_dict print "% Total number of variables:", len(variables) var_num = 1 for var in variables: if var_num % 10 == 0 or var_num == len(variables): print var, ': BITVECTOR(1);' else: print var + ',', var_num += 1 #build the constraints constraint_number = generate_structural_stp_inout(log, var_info) if max_global_obligations > 0: constraint_number += generate_stp_max_obligations( log, variables, max_global_obligations) print "% Structural constraints:", constraint_number #restrict input/output bindings to the ones in the given Cnet cn constraint_number2, binding_info = generate_stp_cn_choices( cn, log, var_info) constraint_number += constraint_number2 in_binding_pos = binding_info.input_binding_positions out_binding_pos = binding_info.output_binding_positions in_mandatory = binding_info.input_mandatory out_mandatory = binding_info.output_mandatory total_bindings = (sum([len(ins) for ins in cn.inset.values()]) + sum([len(outs) for outs in cn.outset.values()])) total_mandatory = len(in_mandatory) + len(out_mandatory) print '%Total bindings:', total_bindings print '%Known mandatory bindings:', total_mandatory #print in_mandatory #print out_mandatory potential_unused = total_bindings - total_mandatory print '%Min unused bindings', min_unused_bindings if min_unused_bindings is not None and min_unused_bindings >= 0: if min_unused_bindings == 0: #find all non-initial activities without mandatory input bindings in_missing = (log.get_alphabet() - set(cn.starting_activities()) - set([x[0] for x in in_mandatory])) #find all non-final activities without mandatory output bindings out_missing = (log.get_alphabet() - set(cn.final_activities()) - set([x[0] for x in out_mandatory])) remaining_mandatory = len(in_missing) + len(out_missing) print '%Generic mandatory bindings (due to obliged connectedness):', remaining_mandatory total_mandatory += remaining_mandatory print '%Total mandatory bindings:', total_mandatory potential_unused = total_bindings - total_mandatory constraint_number += generate_smt_unused_bindings( binding_info, potential_unused / 2) else: constraint_number += generate_smt_unused_bindings( binding_info, min_unused_bindings) #print in_binding_pos #print out_binding_pos print "%Total number of constraints:", constraint_number print print "QUERY (FALSE);" print "COUNTEREXAMPLE;" return potential_unused
def fitting_cases_in_skeleton(log, skeleton): """Returns a log containing the sequences that would fit in any C-net using the arcs in [skeleton] (regardless of the actual bindings that would be required). To speed up the checking, all sequences whose immediately follows relation is inside [skeleton] are automatically accepted. Similarly if none of its predecessors or successors appears in the skeleton, then the case is sure non-fitting.""" # Be careful with extended strategies. For instance, assume that we check # that, for each activity, it must have a relation with at least one # predecessor activity in the case, and the same for successors). # # This is an overapproximation, since activities could 'steal' obligations # needed by other occurrences of the same activity. e.g., saa with skeleton # (s,a) would pass the check, but will not be really fitting. However, cases = log.get_uniq_cases() set_skeleton = set(skeleton) new_cases = defaultdict(int) old_stdout = sys.stdout stpfile = 'simulate.stp' for case_num, (case, occ) in enumerate(cases.iteritems()): imm_follows = True for i, act in enumerate(case[:-1]): if (act,case[i+1]) not in set_skeleton: imm_follows = False break if not imm_follows: #see if it is directly non-fitting possible = True for i, act in enumerate(case): if i < len(case)-1: shared_succ = [(a,b) for a,b in skeleton if a==act and b in case[i+1:]] if not shared_succ: possible = False break if i > 0: shared_pred = [(a,b) for a,b in skeleton if b==act and a in case[:i]] if not shared_pred: possible = False break if possible: #check with SMT singleton_log = Log(cases=[case]) sys.stdout = open(stpfile,'w') var_info = boolean_variables({case:1}, exclusive_use_arcs=skeleton) variables = var_info.variables print "% Total number of variables:", len(variables) for i in xrange(0,len(variables),10): print ','.join(variables[i:i+10]),': BITVECTOR(1);' generate_structural_stp_inout(singleton_log, var_info) print print "QUERY (FALSE);" print "COUNTEREXAMPLE;" sys.stdout = old_stdout stpoutput = subprocess.check_output( [stp_solver, stpfile] ) smt_verified = 'Invalid' in stpoutput else: print 'Unique case {0} is trivially NON-replayable'.format(case_num) else: print 'Unique case {0} is trivially replayable'.format(case_num) if imm_follows or (possible and smt_verified): new_cases[copy.copy(case)] = occ return Log(uniq_cases=new_cases)
shared_pred = [(a, b) for a, b in skeleton if b == act and a in case[:i]] if not shared_pred: possible = False break if possible: #check with SMT singleton_log = Log(cases=[case]) sys.stdout = open(stpfile, 'w') var_info = boolean_variables({case: 1}, exclusive_use_arcs=skeleton) variables = var_info.variables print "% Total number of variables:", len(variables) for i in xrange(0, len(variables), 10): print ','.join(variables[i:i + 10]), ': BITVECTOR(1);' generate_structural_stp_inout(singleton_log, var_info) print print "QUERY (FALSE);" print "COUNTEREXAMPLE;" sys.stdout = old_stdout stpoutput = subprocess.check_output([stp_solver, stpfile]) smt_verified = 'Invalid' in stpoutput else: print 'Unique case {0} is trivially NON-replayable'.format( case_num) else: print 'Unique case {0} is trivially replayable'.format(case_num) if imm_follows or (possible and smt_verified): new_cases[copy.copy(case)] = occ return Log(uniq_cases=new_cases)
def generate_smt_from_cn_and_log( cn, log, var_info, min_unused_bindings=None, max_global_obligations=0 ): """Main function to generate a CVC file (to stdout) that represents the C-net simulation problem. The file can then be fed to the STP solver. Returns the number of potentially unused bindings in the C-net. [cn] C-net to simulate. [log] log to simulate. Must fit inside [cn] [min_unused_bindings] Minimum number of required unused bindings. If 0, then compute the number of mandatory bindings and the number of potentially unused bindings. Use this latter value halfed as required number of minimum unused bindings. Use None if no minimum number of bindings is required. [max_global_obligations] If > 0, restricts the maximum number of generated obligations during the simulation of the log. """ #print the variables traces = log.get_uniq_cases() activity_positions = log.get_activity_positions() variables = var_info.variables obligation_dict = var_info.obligation_dict print "% Total number of variables:", len(variables) var_num = 1 for var in variables: if var_num % 10 == 0 or var_num == len(variables): print var, ': BITVECTOR(1);' else: print var+',', var_num += 1 #build the constraints constraint_number = generate_structural_stp_inout(log, var_info) if max_global_obligations > 0: constraint_number += generate_stp_max_obligations( log, variables, max_global_obligations ) print "% Structural constraints:", constraint_number #restrict input/output bindings to the ones in the given Cnet cn constraint_number2, binding_info = generate_stp_cn_choices( cn, log, var_info ) constraint_number += constraint_number2 in_binding_pos = binding_info.input_binding_positions out_binding_pos = binding_info.output_binding_positions in_mandatory = binding_info.input_mandatory out_mandatory = binding_info.output_mandatory total_bindings = (sum([len(ins) for ins in cn.inset.values()]) + sum([len(outs) for outs in cn.outset.values()])) total_mandatory = len(in_mandatory)+len(out_mandatory) print '%Total bindings:', total_bindings print '%Known mandatory bindings:', total_mandatory #print in_mandatory #print out_mandatory potential_unused = total_bindings-total_mandatory print '%Min unused bindings', min_unused_bindings if min_unused_bindings is not None and min_unused_bindings >= 0: if min_unused_bindings == 0: #find all non-initial activities without mandatory input bindings in_missing = (log.get_alphabet()-set(cn.starting_activities())- set([x[0] for x in in_mandatory])) #find all non-final activities without mandatory output bindings out_missing = (log.get_alphabet()-set(cn.final_activities())- set([x[0] for x in out_mandatory])) remaining_mandatory = len(in_missing) + len(out_missing) print '%Generic mandatory bindings (due to obliged connectedness):', remaining_mandatory total_mandatory += remaining_mandatory print '%Total mandatory bindings:', total_mandatory potential_unused = total_bindings-total_mandatory constraint_number += generate_smt_unused_bindings( binding_info, potential_unused/2) else: constraint_number += generate_smt_unused_bindings( binding_info, min_unused_bindings) #print in_binding_pos #print out_binding_pos print "%Total number of constraints:", constraint_number print print "QUERY (FALSE);" print "COUNTEREXAMPLE;" return potential_unused