예제 #1
0
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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
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)
예제 #6
0
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