def generate_STS(self, var_str, init_str, invar_str, trans_str): ts = TS("Additional system") init = [] trans = [] invar = [] sparser = StringParser() for var in var_str: ts.add_state_var(self._define_var(var)) for init_s in init_str: init.append(sparser.parse_formula(init_s)) for invar_s in invar_str: invar.append(sparser.parse_formula(invar_s)) for trans_s in trans_str: trans.append(sparser.parse_formula(trans_s)) ts.init = And(init) ts.invar = And(invar) ts.trans = And(trans) return ts
def parse_string(self, contents:str)->HTS: ''' Parses a string representation of an initial state file ''' hts = HTS("INIT") ts = TS("TS INIT") init = [] for line in contents.split('\n'): line = line.strip() if not line: continue else: res = self.parse_line(line) if res is not None: init.append(res) Logger.msg("Initial state file set concrete values for {} state variables".format(len(init)), 1) ts.init = And(init) ts.invar = TRUE() ts.trans = TRUE() hts.add_ts(ts) return hts
def compile_sts(self, name, params): ts = TS() parsize = params[0] size = None if type(parsize) == str: sparser = StringParser() parsize = sparser.parse_formula(parsize) if parsize.is_constant(): size = parsize.constant_value() if get_type(parsize).is_bv_type(): size = get_type(parsize).width if size is None: Logger.error("Undefined size for symbol \"%s\"" % (params[0])) value = Symbol("%s.value" % name, BVType(size)) ts.add_var(value) ts.trans = EqualsOrIff(value, TS.get_prime(value)) return ts
def parse_string(self, lines): [none, var, state, input, output, init, invar, trans, ftrans] = range(9) section = none inits = TRUE() invars = TRUE() transs = TRUE() ftranss = {} sparser = StringParser() count = 0 vars = set([]) states = set([]) inputs = set([]) outputs = set([]) invar_props = [] ltl_props = [] for line in lines: count += 1 if (line.strip() in ["", "\n"]) or line[0] == T_COM: continue if T_VAR == line[:len(T_VAR)]: section = var continue if T_STATE == line[:len(T_STATE)]: section = state continue if T_INPUT == line[:len(T_INPUT)]: section = input continue if T_OUTPUT == line[:len(T_OUTPUT)]: section = output continue if T_INIT == line[:len(T_INIT)]: section = init continue if T_INVAR == line[:len(T_INVAR)]: section = invar continue if T_TRANS == line[:len(T_TRANS)]: section = trans continue if T_FTRANS == line[:len(T_FTRANS)]: section = ftrans continue if section in [var, state, input, output]: varname, vartype = line[:-2].replace(" ", "").split(":") if varname[0] == "'": varname = varname[1:-1] vartype = parse_typestr(vartype) vardef = self._define_var(varname, vartype) vars.add(vardef) if section == state: states.add(vardef) if section == input: inputs.add(vardef) if section == output: outputs.add(vardef) if section in [init, invar, trans]: line = line.replace(T_SC, "").strip() qline = quote_names(line, replace_ops=False) if section == init: inits = And(inits, sparser.parse_formula(qline)) if section == invar: invars = And(invars, sparser.parse_formula(qline)) if section == trans: transs = And(transs, sparser.parse_formula(qline)) if section == ftrans: strvar = line[:line.find(":=")] var = sparser.parse_formula( quote_names(strvar, replace_ops=False)) cond_ass = line[line.find(":=") + 2:].strip() ftranss[var] = [] for cond_as in cond_ass.split("{"): if cond_as == "": continue cond = cond_as[:cond_as.find(",")] ass = cond_as[cond_as.find(",") + 1:cond_as.find("}")] ftranss[var].append((sparser.parse_formula( quote_names(cond, replace_ops=False)), sparser.parse_formula( quote_names(ass, replace_ops=False)))) hts = HTS("STS") ts = TS() ts.vars = vars ts.state_vars = states ts.input_vars = inputs ts.output_vars = outputs ts.init = inits ts.invar = invars ts.trans = transs ts.ftrans = ftranss hts.add_ts(ts) return (hts, invar_props, ltl_props)
def generate_HTS(self, module, modulesdic): hts = HTS(module.name) ts = TS("TS %s" % module.name) init = [] trans = [] invar = [] params = [] sparser = StringParser() (vars, states, inputs, outputs) = self._collect_sub_variables(module, modulesdic, path=[], varlist=[], statelist=[], inputlist=[], outputlist=[]) for var in vars: ts.add_var(self._define_var(var, module.name)) for var in states: ts.add_state_var(self._define_var(var, module.name)) for var in inputs: ts.add_input_var(self._define_var(var, module.name)) for var in outputs: ts.add_output_var(self._define_var(var, module.name)) self._check_parameters(module, modulesdic, ts.vars) for par in module.pars: assert len(par) == 2, "Expecting a variable" hts.add_param(self._define_var((par[0], par[1]), module.name)) for init_s in module.init: formula = sparser.parse_formula(quote_names(init_s, module.name), False) init.append(formula) for invar_s in module.invar: formula = sparser.parse_formula(quote_names(invar_s, module.name), False) invar.append(formula) for trans_s in module.trans: formula = sparser.parse_formula(quote_names(trans_s, module.name), False) trans.append(formula) for sub in module.subs: hts.add_sub(sub[0], self.generate_HTS(modulesdic[sub[1]], modulesdic), tuple([v[0] for v in sub[2]])) ts.init = And(init) ts.invar = And(invar) ts.trans = And(trans) hts.add_ts(ts) return hts
def solve_problems(self, problems, config): encoder_config = self.problems2encoder_config(config, problems) self.sparser = StringParser(encoder_config) self.lparser = LTLParser() self.coi = ConeOfInfluence() invar_props = [] ltl_props = [] si = False if len(problems.symbolic_inits) == 0: problems.symbolic_inits.add(si) HTSM = 0 HTS2 = 1 HTSD = (HTSM, si) model_extension = config.model_extension if problems.model_extension is None else problems.model_extension assume_if_true = config.assume_if_true or problems.assume_if_true cache_files = config.cache_files or problems.cache_files clean_cache = config.clean_cache modifier = None if model_extension is not None: modifier = lambda hts: ModelExtension.extend( hts, ModelModifiersFactory.modifier_by_name(model_extension)) # generate systems for each problem configuration systems = {} for si in problems.symbolic_inits: encoder_config.symbolic_init = si (systems[(HTSM, si)], invar_props, ltl_props) = self.parse_model(problems.relative_path, \ problems.model_file, \ encoder_config, \ "System 1", \ modifier, \ cache_files=cache_files, \ clean_cache=clean_cache) if problems.equivalence is not None: (systems[(HTS2, si)], _, _) = self.parse_model(problems.relative_path, \ problems.equivalence, \ encoder_config, \ "System 2", \ cache_files=cache_files, \ clean_cache=clean_cache) else: systems[(HTS2, si)] = None if config.safety or config.problems: for invar_prop in invar_props: inv_prob = problems.new_problem() inv_prob.verification = VerificationType.SAFETY inv_prob.name = invar_prop[0] inv_prob.description = invar_prop[1] inv_prob.formula = invar_prop[2] problems.add_problem(inv_prob) if config.ltl or config.problems: for ltl_prop in ltl_props: ltl_prob = problems.new_problem() ltl_prob.verification = VerificationType.LTL ltl_prob.name = ltl_prop[0] ltl_prob.description = ltl_prop[1] ltl_prob.formula = ltl_prop[2] problems.add_problem(ltl_prob) if HTSD in systems: problems._hts = systems[HTSD] for problem in problems.problems: problem.hts = systems[(HTSM, problem.symbolic_init)] if problems._hts is None: problems._hts = problem.hts problem.hts2 = systems[(HTS2, problem.symbolic_init)] if problems._hts2 is None: problems._hts2 = problem.hts2 problem.vcd = problems.vcd or config.vcd or problem.vcd problem.abstract_clock = problems.abstract_clock or config.abstract_clock problem.add_clock = problems.add_clock or config.add_clock problem.coi = problems.coi or config.coi problem.run_coreir_passes = problems.run_coreir_passes problem.relative_path = problems.relative_path problem.cardinality = max(problems.cardinality, config.cardinality) if not problem.full_trace: problem.full_trace = problems.full_trace if not problem.trace_vars_change: problem.trace_vars_change = problems.trace_vars_change if not problem.trace_all_vars: problem.trace_all_vars = problems.trace_all_vars if not problem.clock_behaviors: clk_bhvs = [ p for p in [problems.clock_behaviors, config.clock_behaviors] if p is not None ] if len(clk_bhvs) > 0: problem.clock_behaviors = ";".join(clk_bhvs) if not problem.generators: problem.generators = config.generators Logger.log( "Solving with abstract_clock=%s, add_clock=%s" % (problem.abstract_clock, problem.add_clock), 2) if problem.trace_prefix is not None: problem.trace_prefix = "".join( [problem.relative_path, problem.trace_prefix]) if config.time or problems.time: timer_solve = Logger.start_timer("Problem %s" % problem.name, False) try: self.__solve_problem(problem, config) if problem.verification is None: Logger.log("Unset verification", 2) continue Logger.msg(" %s\n" % problem.status, 0, not (Logger.level(1))) if (assume_if_true) and \ (problem.status == VerificationStatus.TRUE) and \ (problem.assumptions == None) and \ (problem.verification == VerificationType.SAFETY): ass_ts = TS("Previous assumption from property") if TS.has_next(problem.formula): ass_ts.trans = problem.formula else: ass_ts.invar = problem.formula problem.hts.reset_formulae() problem.hts.add_ts(ass_ts) if config.time or problems.time: problem.time = Logger.get_timer(timer_solve, False) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def solve_problems(self, problems_config: ProblemsManager) -> None: general_config = problems_config.general_config model_extension = general_config.model_extension assume_if_true = general_config.assume_if_true self.sparser = StringParser(general_config) self.lparser = LTLParser() self.coi = ConeOfInfluence() modifier = None if general_config.model_extension is not None: modifier = lambda hts: ModelExtension.extend( hts, ModelModifiersFactory.modifier_by_name(general_config. model_extension)) # generate main system system hts, invar_props, ltl_props = self.parse_model( general_config.model_files, problems_config.relative_path, general_config, "System 1", modifier) # Generate second models if any are necessary for problem in problems_config.problems: if problem.verification == VerificationType.EQUIVALENCE: if problem.equal_to is None: raise RuntimeError( "No second model for equivalence " "checking provided for problem {}".format( problem.name)) hts2, _, _ = self.parse_model(problem.equal_to, problems_config.relative_path, general_config, "System 2", modifier) problems_config.add_second_model(problem, hts2) # TODO : contain these types of passes in functions # they should be registered as passes if general_config.init is not None: iparser = InitParser() init_hts, inv_a, ltl_a = iparser.parse_file( general_config.init, general_config) assert inv_a is None and ltl_a is None, "Not expecting assertions from init state file" # remove old inits for ts in hts.tss: ts.init = TRUE() hts.combine(init_hts) hts.single_init(rebuild=True) # set default bit-wise initial values (0 or 1) if general_config.default_initial_value is not None: def_init_val = int(general_config.default_initial_value) try: if int(def_init_val) not in {0, 1}: raise RuntimeError except: raise RuntimeError( "Expecting 0 or 1 for default_initial_value," "but received {}".format(def_init_val)) def_init_ts = TS("Default initial values") new_init = [] initialized_vars = get_free_variables(hts.single_init()) state_vars = hts.state_vars num_def_init_vars = 0 num_state_vars = len(state_vars) const_arr_supported = True if hts.logic == L_ABV: for p in problems_config.problems: if p.solver_name not in CONST_ARRAYS_SUPPORT: const_arr_supported = False Logger.warning( "Using default_initial_value with arrays, " "but one of the selected solvers, " "{} does not support constant arrays. " "Any assumptions on initial array values will " "have to be done manually".format( problem.solver_name)) break for sv in state_vars - initialized_vars: if sv.get_type().is_bv_type(): width = sv.get_type().width if int(def_init_val) == 1: val = BV((2**width) - 1, width) else: val = BV(0, width) num_def_init_vars += 1 elif sv.get_type().is_array_type() and \ sv.get_type().elem_type.is_bv_type() and \ const_arr_supported: svtype = sv.get_type() width = svtype.elem_type.width if int(def_init_val) == 1: val = BV((2**width) - 1, width) else: val = BV(0, width) # create a constant array with a default value val = Array(svtype.index_type, val) else: continue def_init_ts.add_state_var(sv) new_init.append(EqualsOrIff(sv, val)) def_init_ts.set_behavior(simplify(And(new_init)), TRUE(), TRUE()) hts.add_ts(def_init_ts) Logger.msg( "Set {}/{} state elements to zero " "in initial state\n".format(num_def_init_vars, num_state_vars), 1) problems_config.hts = hts # TODO: Update this so that we can control whether embedded assertions are solved automatically if not general_config.skip_embedded: for invar_prop in invar_props: problems_config.add_problem( verification=VerificationType.SAFETY, name=invar_prop[0], description=invar_prop[1], properties=invar_prop[2]) self.properties.append(invar_prop[2]) for ltl_prop in ltl_props: problems_config.add_problem(verification=VerificationType.LTL, name=invar_prop[0], description=invar_prop[1], properties=invar_prop[2]) self.properties.append(ltl_prop[2]) Logger.log( "Solving with abstract_clock=%s, add_clock=%s" % (general_config.abstract_clock, general_config.add_clock), 2) # ensure the miter_out variable exists miter_out = None for problem in problems_config.problems: if problem.name is not None: Logger.log( "\n*** Analyzing problem \"%s\" ***" % (problem.name), 1) Logger.msg("Solving \"%s\" " % problem.name, 0, not (Logger.level(1))) # apply parametric behaviors (such as toggling the clock) # Note: This is supposed to be *before* creating the combined system for equivalence checking # we want this assumption to be applied to both copies of the clock problem_hts = ParametricBehavior.apply_to_problem( problems_config.hts, problem, general_config, self.model_info) if problem.verification == VerificationType.EQUIVALENCE: hts2 = problems_config.get_second_model(problem) problem_hts, miter_out = Miter.combine_systems( hts, hts2, problem.bmc_length, general_config.symbolic_init, problem.properties, True) try: # convert the formulas to PySMT FNodes # lemmas, assumptions and precondition always use the regular parser lemmas, assumptions, precondition = self.convert_formulae( [ problem.lemmas, problem.assumptions, problem.precondition ], parser=self.sparser, relative_path=problems_config.relative_path) if problem.verification != VerificationType.LTL: parser = self.sparser else: parser = self.lparser prop = None if problem.properties is not None: prop = self.convert_formula( problem.properties, relative_path=problems_config.relative_path, parser=parser) assert len(prop) == 1, "Properties should already have been split into " \ "multiple problems but found {} properties here".format(len(prop)) prop = prop[0] self.properties.append(prop) else: if problem.verification == VerificationType.SIMULATION: prop = TRUE() elif (problem.verification is not None) and (problem.verification != VerificationType.EQUIVALENCE): Logger.error( "Property not provided for problem {}".format( problem.name)) if problem.verification == VerificationType.EQUIVALENCE: assert miter_out is not None # set property to be the miter output # if user provided a different equivalence property, this has already # been incorporated in the miter_out prop = miter_out # reset the miter output miter_out = None if precondition: assert len(precondition ) == 1, "There should only be one precondition" prop = Implies(precondition[0], prop) # TODO: keep assumptions separate from the hts # IMPORTANT: CLEAR ANY PREVIOUS ASSUMPTIONS AND LEMMAS # This was previously done in __solve_problem and has been moved here # during the frontend refactor in April 2019 # this is necessary because the problem hts is just a reference to the # overall (shared) HTS problem_hts.assumptions = None problem_hts.lemmas = None # Compute the Cone Of Influence # Returns a *new* hts (not pointing to the original one anymore) if problem.coi: if Logger.level(2): timer = Logger.start_timer("COI") hts = self.coi.compute(hts, prop) if Logger.level(2): Logger.get_timer(timer) if general_config.time: timer_solve = Logger.start_timer( "Problem %s" % problem.name, False) status, trace, traces, region = self.__solve_problem( problem_hts, prop, lemmas, assumptions, problem) # set status for this problem problems_config.set_problem_status(problem, status) # TODO: Determine whether we need both trace and traces assert trace is None or traces is None, "Expecting either a trace or a list of traces" if trace is not None: problem_traces = self.__process_trace( hts, trace, general_config, problem) problems_config.set_problem_traces(problem, problem_traces) if traces is not None: traces_to_add = [] for trace in traces: problem_trace = self.__process_trace( hts, trace, general_config, problem) for pt in problem_trace: traces_to_add.append(pt) problems_config.set_problem_traces(problem, traces_to_add) if problem.verification == VerificationType.PARAMETRIC: assert region is not None problems_config.set_problem_region(problem, region) if status is not None: Logger.msg(" %s\n" % status, 0, not (Logger.level(1))) if (assume_if_true) and \ (status == VerificationStatus.TRUE) and \ (problem.assumptions == None) and \ (problem.verification == VerificationType.SAFETY): # TODO: relax the assumption on problem.assumptions # can still add it, just need to make it an implication ass_ts = TS("Previous assumption from property") if TS.has_next(prop): ass_ts.trans = prop else: ass_ts.invar = prop # add assumptions to main system problem_hts.reset_formulae() problem_hts.add_ts(ass_ts) if general_config.time: problems_config.set_problem_time( problem, Logger.get_timer(timer_solve, False)) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def parse_string(self, lines): [none, var, state, input, output, init, invar, trans] = range(8) section = none inits = TRUE() invars = TRUE() transs = TRUE() sparser = StringParser() count = 0 vars = set([]) states = set([]) inputs = set([]) outputs = set([]) invar_props = [] ltl_props = [] for line in lines: count += 1 if line.strip() in ["", "\n"]: continue if T_VAR == line[:len(T_VAR)]: section = var continue if T_STATE == line[:len(T_STATE)]: section = state continue if T_INPUT == line[:len(T_INPUT)]: section = input continue if T_OUTPUT == line[:len(T_OUTPUT)]: section = output continue if T_INIT == line[:len(T_INIT)]: section = init continue if T_INVAR == line[:len(T_INVAR)]: section = invar continue if T_TRANS == line[:len(T_TRANS)]: section = trans continue if section in [var, state, input, output]: line = line[:-2].replace(" ", "").split(":") varname, vartype = line[0], (line[1][:-1].split("(")) if varname[0] == "'": varname = varname[1:-1] vardef = self._define_var(varname, vartype) vars.add(vardef) if section == state: states.add(vardef) if section == input: inputs.add(vardef) if section == output: outputs.add(vardef) if section in [init, invar, trans]: qline = quote_names(line[:-2], replace_ops=False) if section == init: inits = And(inits, sparser.parse_formula(qline)) if section == invar: invars = And(invars, sparser.parse_formula(qline)) if section == trans: transs = And(transs, sparser.parse_formula(qline)) hts = HTS("STS") ts = TS() ts.vars = vars ts.state_vars = states ts.input_vars = inputs ts.output_vars = outputs ts.init = inits ts.invar = invars ts.trans = transs hts.add_ts(ts) return (hts, invar_props, ltl_props)
def compute(self, hts, prop): Logger.log("Building COI", 1) self._build_var_deps(hts) coi_vars = set(self._free_variables(prop)) if (len(coi_vars) < 1) or (self.var_deps == {}): return hts if hts.assumptions is not None: for assumption in hts.assumptions: for v in self._free_variables(assumption): coi_vars.add(v) if hts.lemmas is not None: for lemma in hts.lemmas: for v in self._free_variables(lemma): coi_vars.add(v) coits = TS("COI") coi_vars = list(coi_vars) i = 0 visited = set([]) while i < len(coi_vars): var = coi_vars[i] if (var in visited) or (var not in self.var_deps): i += 1 continue coi_vars = coi_vars[:i + 1] + list( self.var_deps[var]) + coi_vars[i + 1:] visited.add(var) i += 1 coi_vars = frozenset(coi_vars) trans = list( conjunctive_partition(hts.single_trans(include_ftrans=True))) invar = list( conjunctive_partition(hts.single_invar(include_ftrans=True))) init = list(conjunctive_partition(hts.single_init())) coits.trans = [ f for f in trans if self._intersect(coi_vars, self._free_variables(f)) ] coits.invar = [ f for f in invar if self._intersect(coi_vars, self._free_variables(f)) ] coits.init = [ f for f in init if self._intersect(coi_vars, self._free_variables(f)) ] Logger.log("COI statistics:", 1) Logger.log(" Vars: %s -> %s" % (len(hts.vars), len(coi_vars)), 1) Logger.log(" Init: %s -> %s" % (len(init), len(coits.init)), 1) Logger.log(" Invar: %s -> %s" % (len(invar), len(coits.invar)), 1) Logger.log(" Trans: %s -> %s" % (len(trans), len(coits.trans)), 1) coits.trans = And(coits.trans) coits.invar = And(coits.invar) coits.init = And(coits.init) coits.vars = set([]) for bf in [init, invar, trans]: for f in bf: for v in self._free_variables(f): coits.vars.add(v) coits.input_vars = set([v for v in coi_vars if v in hts.input_vars]) coits.output_vars = set([v for v in coi_vars if v in hts.output_vars]) coits.state_vars = set([v for v in coi_vars if v in hts.state_vars]) new_hts = HTS("COI") new_hts.add_ts(coits) if self.save_model: printer = HTSPrintersFactory.printer_by_name("STS") with open("/tmp/coi_model.ssts", "w") as f: f.write(printer.print_hts(new_hts, [])) return new_hts