def poission_model(): module = Module("poission process") module.addConstant(Constant('r', None)) # variable definition v = BoundedVariable('n', 0, None, int, False) # n: int init 0; module.add_variable(v) # command definition comm = Command( '', lambda vs, cs: True, lambda vs, cs: vs['n'].set_value( vs['n'].get_value() + 1), module, module.getConstant("r"), CommandKind.NONE, None) module.addCommand(comm) # 增加label表示n>=4这个ap labels = dict() def nge4(vs, cs): return vs['n'] >= 4 labels['nge4'] = nge4 model = ModulesFile(ModelType.CTMC, modules=[module], labels=labels) return model
def _timermodule(self): config = self.config module = Module('TIME') module.add_variable( BoundedVariable('day', 1, range(1, config.getParam('DURATION_IN_DAY') + 1), int)) module.add_variable( BoundedVariable('timer_turn', False, set([True, False]), bool)) def _timer_action_guard(vs, cs): day_val = vs['day'].get_value() DAY_MAX = self.config.getParam("DURATION_IN_DAY") t_turn = vs['timer_turn'].get_value() return day_val < DAY_MAX and t_turn == True def action(vs, cs): # vs['day'].set_value(vs['day'].get_value() + 1) vs['day'].incr() vs['timer_turn'].set_value(False) module.addCommand( Command('inc day', _timer_action_guard, action, module, lambda: 1.0)) return module
def _sbmodule(self): config = self.config module = Module('SB') module.addConstant(config.getParam('SB_K')) module.addConstant(config.getParam('SB_A_MU')) module.addConstant(config.getParam('SB_A_SIGMA')) module.addConstant(config.getParam('SB_B')) module.addConstant(config.getParam('SB_P_THRESHOLD')) module.add_variable(BoundedVariable('sb_status', 1, range(2), int)) def _module_action_guard(vs, cs): timer_turn = vs['timer_turn'].get_value() sb_status = vs['sb_status'].get_value() s3r_status = vs['s3r_status'].get_value() bcr_status = vs['bcr_status'].get_value() bdr_status = vs['bdr_status'].get_value() return timer_turn == False and sb_status == 1 and s3r_status == 1 and bcr_status == 1 and bdr_status == 1 def faction(vs, cs): vs['sb_status'].set_value(0) vs['timer_turn'].set_value(True) # normal action def naction(vs, cs): vs['timer_turn'].set_value(True) def f(day_var): def inner(): day = day_var.get_value() dose = module.getConstant('SB_K') * config.getParam( 'SCREEN_THICKNESS') * day / 365.0 x = (-module.getConstant('SB_P_THRESHOLD') + 1) / \ (log(module.getConstant('SB_B') * dose + 1)) std_x = 1.0 / (module.getConstant('SB_A_SIGMA') / (-module.getConstant('SB_A_MU') + x)) return 1 - pcf(std_x) return inner def n(day_var): def inner(): ff = f(day_var) return 1 - ff() return inner if not hasattr(self, 'timer'): self.timer = self._timermodule() module.addCommand( Command('sb_fail_cmd', _module_action_guard, faction, module, f(self.timer.getVariable('day')))) module.addCommand( Command('sb_nrl_cmd', _module_action_guard, naction, module, n(self.timer.getVariable('day')))) return module
def _bcrmodule(self): config = self.config module = Module('BCR') module.add_variable(BoundedVariable('bcr_status', 1, range(2), int)) def _module_action_guard(vs, cs): timer_turn = vs['timer_turn'].get_value() sb_status = vs['sb_status'].get_value() s3r_status = vs['s3r_status'].get_value() bcr_status = vs['bcr_status'].get_value() bdr_status = vs['bdr_status'].get_value() return timer_turn == False and sb_status == 1 and s3r_status == 1 and bcr_status == 1 and bdr_status == 1 def faction(vs, cs): vs['bcr_status'].set_value(0) vs['timer_turn'].set_value(True) def naction(vs, cs): vs['timer_turn'].set_value(True) def f(day_var): def inner(): day = day_var.get_value() dose = config.getParam('S3R_K') / config.getParam( 'SCREEN_THICKNESS') * (day / 365.0) x = config.getParam('S3R_DELTAV_THRESHOLD') / ( config.getParam('S3R_B') * pow(e, config.getParam('S3R_B') * dose)) std_x = (-config.getParam('S3R_A_MU') + x) / \ config.getParam('S3R_A_SIGMA').get_value() p = 1 - pcf(std_x) # logger.info('day:{0}, bcr failure prob:{1}'.format(day, p)) return p return inner def n(day_var): def inner(): ff = f(day_var) return 1 - ff() return inner if not hasattr(self, 'timer'): self.timer = self._timermodule() module.addCommand( Command('bcr_fail_cmd', _module_action_guard, faction, module, f(self.timer.getVariable('day')))) module.addCommand( Command('bcr_nrl_cmd', _module_action_guard, naction, module, n(self.timer.getVariable('day')))) return module
class PRISMParser(object): def __init__(self): # 表示当前是否正在解析module模块 # 用途: 值为false时,对boolean_expression的解析不会将结果保存为guard self._parsing_module = False ''' 当parser分析到一行Command时,可能最后会解析成多个Command对象 每个Command对象包含以下信息:name, guard, prob, action, module 目前默认不设置Command的name,即name为空 module在每次解析到LB时进行初始化表示当前正在生成的module prob在解析到prob_expr的时候进行设置,prob是一个函数对象,因为需要根据判定对象(变量)的值进行实时计算 最重要的Command对象在解析到prob_update时进行初始化,因为从Command的构成上讲, 一个prob + update就构成了一个Command对象。 之所以要在BasicParser中保存module和guard对象是因为 它们是被多个Command对象所共享的。 ''' self._m = None # module self._g = None # guard self.binary_op_map = { '+': lambda x, y: x + y, '-': lambda x, y: x - y, '*': lambda x, y: x * y, '/': lambda x, y: x / y } self.func_map = {'stdcdf': pcf, 'floor': floor, 'ceil': ceil} self._type_map = {'int': int, 'double': float, 'bool': bool} self._cname = None # command sync name self._vcf_map = defaultdict( lambda: None) # variable, constant and function map self._lexer = MyLexer().lexer def unsure_parameters(self): names = [] for name, obj_or_func in self._vcf_map.items(): if not callable(obj_or_func): if obj_or_func.get_value() is None: names.append(name) return names def p_statement(self, p): '''statement : model_type_statement | const_value_statement | module_def_begin_statement | module_def_end_statement | module_var_def_statement | module_command_statement | formula_statement | label_statement''' pass def p_model_type(self, p): '''model_type_statement : DTMC | CTMC''' model_type = [ModelType.DTMC, ModelType.CTMC][p[1] == 'ctmc'] ModelConstructor.set_model(ModulesFile(model_type=model_type)) def p_module_def_begin_statement(self, p): '''module_def_begin_statement : MODULE NAME''' self._parsing_module = True self._m = Module(p[2]) def p_module_def_end_statement(self, p): '''module_def_end_statement : ENDMODULE''' if not self._parsing_module: raise Exception( "Syntax error: end module definition before starting defining it" ) self._parsing_module = False ModelConstructor.get_model().add_module(self._m) def p_const_expression(self, p): '''const_value_statement : CONST INT NAME ASSIGN NUM SEMICOLON | CONST DOUBLE NAME ASSIGN NUM SEMICOLON | CONST BOOL NAME ASSIGN NUM SEMICOLON''' n = p[3] v = self._resolve_literal(p[5], p[2]) c = Constant(n, v, self._type_map[p[2]]) ModelConstructor.get_model().add_constant(c) self._vcf_map[n] = c def p_const_expression1(self, p): '''const_value_statement : CONST INT NAME SEMICOLON | CONST DOUBLE NAME SEMICOLON | CONST BOOL NAME SEMICOLON''' # 支持解析不确定的常量表达式 n = p[3] c = Constant(n, None, self._type_map[p[2]]) ModelConstructor.get_model().add_constant(c) self._vcf_map[n] = c def p_const_expression2(self, p): '''const_value_statement : CONST INT NAME ASSIGN expr SEMICOLON | CONST DOUBLE NAME ASSIGN expr SEMICOLON | CONST BOOL NAME ASSIGN expr SEMICOLON''' n = p[3] expr = copy(p[5]) t = copy(p[2]) # type # there could be unsure parameter in expression def f(): v = self._resolve_literal(expr(), t) return v c = Constant(n, f, self._type_map[t]) ModelConstructor.get_model().add_constant(c) self._vcf_map[n] = c def p_module_var_def_statement(self, p): '''module_var_def_statement : NAME COLON LB expr COMMA expr RB INIT expr SEMICOLON''' v = BoundedIntegerVariable(p[1], p[9], (p[4], p[6])) self._m.add_variable(v) self._vcf_map[v.get_name()] = v def p_module_command_statement(self, p): '''module_command_statement : LB NAME RB boolean_expression THEN updates SEMICOLON''' n = p[2] commands = p[6] for c in commands: c.set_name(n) c.set_mod_name(self._m.get_name()) self._m.add_command(c) def p_module_command_statement1(self, p): '''module_command_statement : LB RB boolean_expression THEN updates SEMICOLON''' n = "" commands = p[5] for c in commands: c.set_name(n) c.set_mod_name(self._m.get_name()) self._m.add_command(c) def p_updates(self, p): '''updates : updates ADD prob_update''' p[0] = list(p[1]) p[0].append(p[3]) def p_updates1(self, p): '''updates : prob_update''' p[0] = list() p[0].append(p[1]) def p_prob_update(self, p): '''prob_update : expr COLON actions''' p[0] = Command(None, copy(self._g), p[1], p[3]) def p_prob_update1(self, p): '''prob_update : actions''' # todo why copy here? p[0] = Command(None, copy(self._g), lambda: 1.0, p[1]) def p_prob_update2(self, p): '''prob_update : TRUE''' p[0] = Command(None, copy(self._g), lambda: 1.0, dict()) def p_actions(self, p): '''actions : actions AND assignment''' p[1].update(p[3]) p[0] = p[1] def p_actions2(self, p): '''actions : assignment''' p[0] = p[1] def p_assignment(self, p): '''assignment : NAME QUOTE ASSIGN expr''' p[0] = {self._vcf_map[p[1]]: p[4]} def p_assignment1(self, p): '''assignment : LP NAME QUOTE ASSIGN expr RP''' p[0] = {self._vcf_map[p[2]]: p[5]} def p_assignment2(self, p): '''assignment : TRUE''' p[0] = {} def p_expr(self, p): '''expr : expr ADD term | expr MINUS term''' f1 = copy(p[1]) f2 = copy(p[3]) op = copy(p[2]) def f(): if not callable(f1) or not callable(f2): raise Exception("Expression must be a function") if op == '-': return f1() - f2() elif op == '+': return f1() + f2() raise Exception("Unrecognized operator {}".format(p[2])) p[0] = f def p_expr2(self, p): '''expr : term''' p[0] = copy(p[1]) def p_term(self, p): '''term : term MUL factor | term DIV factor''' f1 = copy(p[1]) f2 = copy(p[3]) op = copy(p[2]) def f(): if not callable(f1) or not callable(f2): raise Exception("Expression must be a function") if op == "*": return f1() * f2() elif op == '/': return f1() / f2() raise Exception("Unrecognized operator {}".format(op)) p[0] = f def p_term1(self, p): '''term : factor''' p[0] = copy(p[1]) def p_term2(self, p): '''term : boolean_expression QM expr COLON expr''' # 支持条件表达式 condition = copy(p[1]) expr1 = copy(p[3]) expr2 = copy(p[5]) def f(): if not callable(condition): raise Exception("boolean expression must be callable") if condition(self._vcf_map, self._vcf_map): return expr1() else: return expr2() p[0] = f def p_term3(self, p): '''term : LP expr RP''' p[0] = copy(p[2]) def p_factor(self, p): '''factor : NUM''' num = copy(p[1]) p[0] = lambda: num def p_factor1(self, p): '''factor : NAME''' k = copy(p[1]) def f(): if k not in self._vcf_map: raise Exception("Unknown token {}".format(k)) v = self._vcf_map[k] if callable(v): return v() return v.get_value() p[0] = f def p_factor2(self, p): '''factor : NAME LP expr RP''' func_name = p[1] if func_name not in ExpressionHelper.func_map: raise Exception("Unknown function {}".format(func_name)) if not callable(p[3]): raise Exception("Expression must be callable") param = copy(p[3]) def f(): return ExpressionHelper.func_map[func_name](param()) p[0] = f def p_factor3(self, p): '''factor : LP expr RP''' p[0] = p[2] def p_factor4(self, p): '''factor : NAME LP params RP''' fname = p[1] if fname not in ExpressionHelper.func_map: raise Exception("Unknown function {}".format(fname)) params = copy(p[3]) def f(): return ExpressionHelper.func_map[fname](*params) p[0] = f def p_params(self, p): '''params : params COMMA expr''' p[1].append(p[3]) p[0] = p[1] def p_params1(self, p): '''params : expr''' p[0] = list([p[1]]) def p_boolean_expression(self, p): '''boolean_expression : boolean_expression AND boolean_expression | boolean_expression OR boolean_expression | boolean_expression_unit''' if len(p) == 4: op = p[2] f1 = copy(p[1]) f2 = copy(p[3]) def bool_and(vs, cs): return f1(vs, cs) and f2(vs, cs) def bool_or(vs, cs): return f1(vs, cs) or f2(vs, cs) if op == '&': func = bool_and else: func = bool_or p[0] = func elif len(p) == 2: p[0] = p[1] if self._parsing_module: # the parser is parsing a module, since label must be defined outside of any module self._g = p[0] def p_boolean_expression1(self, p): '''boolean_expression : LP boolean_expression RP''' # 单独的一个boolean expression不允许加括号 p[0] = p[2] def p_boolean_expression_unit(self, p): '''boolean_expression_unit : expr GT expr | expr LT expr | expr GE expr | expr LE expr | expr EQ expr | expr NEQ expr''' op1 = copy(p[1]) op2 = copy(p[3]) op = copy(p[2]) def f(vs, cs): if not callable(op1) or not callable(op2): raise Exception("expression must be callable") bool_func = self._gen_bool_func(op) return bool_func(op1(), op2()) p[0] = f def p_boolean_expression_unit1(self, p): '''boolean_expression_unit : TRUE''' p[0] = lambda vs, cs: True def p_boolean_expression_unit2(self, p): '''boolean_expression_unit : FALSE''' p[0] = lambda vs, cs: False def p_formula_statement(self, p): '''formula_statement : FORMULA NAME ASSIGN expr SEMICOLON''' self._vcf_map[p[2]] = p[4] def p_label_statement(self, p): '''label_statement : LABEL NAME ASSIGN boolean_expression SEMICOLON''' ModelConstructor.get_model().add_label(p[2], p[4]) def _resolve_literal(self, v, t): # v: value in string # t: type in string: int, double or bool if t not in self._type_map: raise Exception("Unrecognized type {}".format(t)) return self._type_map[t](v) def _gen_bool_func(self, op): ''' 根据传入的op(str)返回相应的函数 :param op: str :return: func ''' eqfunc = lambda op1, op2: op1 == op2 ltfunc = lambda op1, op2: op1 < op2 lefunc = lambda op1, op2: ltfunc(op1, op2) or eqfunc(op1, op2) gtfunc = lambda op1, op2: not lefunc(op1, op2) nefunc = lambda op1, op2: not eqfunc(op1, op2) gefunc = lambda op1, op2: gtfunc(op1, op2) or eqfunc(op1, op2) map = { '<': ltfunc, '<=': lefunc, '>': gtfunc, '>=': gefunc, '==': eqfunc, '!=': nefunc } if op not in map: raise Exception("Unsupported boolean operation {}".format(op)) return map[op] def parse_model(self, filepath): commentremoved = clear_comment(filepath) lines = [] with open(commentremoved) as f: for l in f: lines.append(l) if self.parser: for line in lines: # tokens = [] # if line.find("[]") != -1: # lexer.input(line) # for token in lexer: # tokens.append(token) # print tokens self.parser.parse(line, lexer=self._lexer) def parse_line(self, line): if not line or not len(line): return self.parser.parse(line, lexer=self._lexer) def build(self): cur_dir = dirname(realpath(__file__)) self.tokens = MyLexer.tokens self.parser = yacc.yacc(module=self, outputdir=cur_dir)
def sps_model_dtmc(duration): # duration: timer.day的最大值:天数 # timer module of the system timer = Module('timer_module') timer.addConstant(Constant('TIME_LIMIT', duration)) day = BoundedVariable( 'day', 1, range(timer.getConstant('TIME_LIMIT').get_value() + 1), int, True) timer_turn = BoundedVariable('timer_turn', True, set([True, False]), bool, True) timer.add_variable(day) timer.add_variable(timer_turn) def incdayaction(vs, cs): vs['day'].set_value(vs['day'].get_value() + 1) vs['timer_turn'].set_value(False) inc_day = Command( 'inc day', lambda vs, cs: vs['timer_turn'] == True and vs['day'] < timer.getConstant('TIME_LIMIT').get_value(), incdayaction, timer, 1.0) timer.addCommand(inc_day) # solar battery module of the system sb = Module("solar battery module") # set initial value None to denote uncertainty screenthick = Constant('SCREEN_THICKNESS', None) sb.addConstant(screenthick) # the dose of one year: dose = K_SB * thickness sb.addConstant(Constant('SB_K', 0.0039)) sb.addConstant(Constant('SB_B', 12)) sb.addConstant(Constant('SB_P_THRESHOLD', 0.7)) sb.addConstant(Constant('SB_A_MU', 0.1754)) sb.addConstant(Constant('SB_A_SIGMA', 0.02319029 * 21)) # variables sb_status = BoundedVariable('sb_status', 1, range(2), int, True) sb.add_variable(sb_status) def failaction(vs, cs): vs['sb_status'].set_value(0) vs['timer_turn'].set_value(True) # use closure to delay the computation of fail rate def f1(day_var): def failprobsb(): # return the failure probability of solar battery after day-dose niel_dose = sb.getConstant('SB_K').get_value() * sb.getConstant( 'SCREEN_THICKNESS').get_value() * (day_var.get_value() / 365.0) x = (1 - sb.getConstant('SB_P_THRESHOLD').get_value()) / \ log(1 + niel_dose * sb.getConstant('SB_B').get_value()) std_x = (x - sb.getConstant('SB_A_MU').get_value() / sb.getConstant('SB_A_SIGMA').get_value()) temp = pcf(std_x) i = id(day_var) return 1 - temp return failprobsb def f1n(day_var): def normalprobsb(): niel_dose = sb.getConstant('SB_K').get_value() * sb.getConstant( 'SCREEN_THICKNESS').get_value() * (day_var.get_value() / 365.0) x = (1 - sb.getConstant('SB_P_THRESHOLD').get_value()) / \ log(1 + niel_dose * sb.getConstant('SB_B').get_value()) std_x = (x - sb.getConstant('SB_A_MU').get_value() / sb.getConstant('SB_A_SIGMA').get_value()) return pcf(std_x) return normalprobsb comm_fail = Command( 'solar battery failure command', lambda vs, cs: vs['sb_status'] == 1 and vs['timer_turn'] == False, failaction, sb, f1(timer.getVariable('day')), kind=CommandKind.FAILURE) sb.addCommand(comm_fail) comm_normal = Command( 'solar battery stay-normal command', lambda vs, cs: vs['sb_status'] == 1 and vs['timer_turn'] == False, lambda vs, cs: vs['timer_turn'].set_value(True), sb, f1n(timer.getVariable('day'))) sb.addCommand(comm_normal) s3r = Module('S3R模块') s3r.addConstant(screenthick) s3r.addConstant(Constant('S3R_K', 200.5 / 3.0)) # s3r, bdr, bcr三个模块均摊电离能损 s3r.addConstant(Constant('S3R_DELTAV_THRESHOLD', 450)) s3r.addConstant(Constant('S3R_A_MU', 570.8 * 18)) s3r.addConstant(Constant('S3R_A_SIGMA', 6.7471 * 120)) s3r.addConstant(Constant('S3R_B', 0.01731)) s3r_status = BoundedVariable('s3r_status', 1, range(2), int, True) s3r.add_variable(s3r_status) def s3rfailaction(vs, cs): vs['s3r_status'].set_value(0) vs['timer_turn'].set_value(True) def f2(day_var): def failprobs3r(): iel_dose = day_var.get_value() / 365.0 * (s3r.getConstant('S3R_K').get_value() / \ s3r.getConstant('SCREEN_THICKNESS').get_value()) x = s3r.getConstant('S3R_DELTAV_THRESHOLD').get_value() / ( s3r.getConstant('S3R_B').get_value() * pow(e, s3r.getConstant('S3R_B').get_value() * iel_dose)) std_x = (x - s3r.getConstant('S3R_A_MU').get_value()) / \ s3r.getConstant('S3R_A_SIGMA').get_value() return 1 - pcf(std_x) return failprobs3r def f2n(day_var): def normalprobs3r(): iel_dose = day_var.get_value() / 365.0 * (s3r.getConstant('S3R_K').get_value() / \ s3r.getConstant('SCREEN_THICKNESS').get_value()) x = s3r.getConstant('S3R_DELTAV_THRESHOLD').get_value() / ( s3r.getConstant('S3R_B').get_value() * pow(e, s3r.getConstant('S3R_B').get_value() * iel_dose)) std_x = (x - s3r.getConstant('S3R_A_MU').get_value()) / \ s3r.getConstant('S3R_A_SIGMA').get_value() return pcf(std_x) return normalprobs3r s3r_comm_fail = Command( 's3r failure command', lambda vs, cs: vs['s3r_status'] == 1 and vs['timer_turn'] == False, s3rfailaction, s3r, f2(timer.getVariable('day')), CommandKind.FAILURE) s3r.addCommand(s3r_comm_fail) s3r_comm_norm = Command( 's3r normal command', lambda vs, cs: vs['timer_turn'] == False and vs['s3r_status'] == 1, lambda vs, cs: vs['timer_turn'].set_value(True), s3r, f2n(timer.getVariable('day'))) s3r.addCommand(s3r_comm_norm) def failureif(vs, cs): return vs['s3r_status'] == 0 or vs['sb_status'] == 0 labels = dict() labels['up'] = lambda vs, cs: vs['s3r_status'] == 1 and vs['sb_status' ] == 1 labels['failure'] = lambda vs, cs: vs['s3r_status'] == 0 or vs['sb_status' ] == 0 i = id(timer.getVariable('day')) model = ModulesFile.ModulesFile(ModelType.DTMC, modules=[timer, sb, s3r], failureCondition=failureif, labels=labels, stopCondition=failureif) ii = id(model.localVars['day']) assert i == ii return model