Ejemplo n.º 1
0
    def to_formula(self):
        """Given list of variables, compute constraints

        :param variables: Variables for each dimension
        :return: A formula specifying the points inside the hyperrectangle
        :rtype: pc.Constraint or pc.Formula
        """
        constraint = pc.Constraint(True)
        for par in self.get_parameters():
            constraint = constraint & pc.Constraint(
                pc.Polynomial(par.variable) - self[par], pc.Relation.EQ)
        return constraint
Ejemplo n.º 2
0
 def to_formula(self, variables):
     """Given list of variables, compute constraints
     
     :param variables: Variables for each dimension
     :return: A formula specifying the points inside the hyperrectangle
     :rtype: pc.Constraint or pc.Formula
     """
     constraint = pc.Constraint(True)
     for variable, interval in zip(variables, self.intervals):
         if interval.left_bound != -inf:
             lbound_relation = pc.Relation.GEQ if interval.left_bound_type(
             ) == BoundType.closed else pc.Relation.GREATER
             lbound = pc.Constraint(
                 pc.Polynomial(variable) - interval.left_bound(),
                 lbound_relation)
             constraint = constraint & lbound
         if interval.right_bound != inf:
             rbound_relation = pc.Relation.LEQ if interval.right_bound_type(
             ) == BoundType.closed else pc.Relation.LESS
             rbound = pc.Constraint(
                 pc.Polynomial(variable) - interval.right_bound(),
                 rbound_relation)
             constraint = constraint & rbound
     return constraint
Ejemplo n.º 3
0
def compute_solution_function(state, export):
    logging.info("Compute the rational function using " + state.mc.name() +
                 " " + state.mc.version())
    state.mc.load_model(state.problem_description.model,
                        state.problem_description.constants)
    state.mc.set_pctl_formula(state.problem_description.property)
    result = state.mc.get_rational_function()

    #state.problem_description.parameters.update_variables(result.ratfunc.gather_variables())

    # Mapping for parameters from solution function
    def get_matching_model_parameter(model_parameters, variable_name):
        """Return matching parameter or None."""
        return next((v for v in model_parameters if v.name == variable_name),
                    None)

    parameter_mapping = {}
    model_parameters = state.problem_description.parameters
    for sf_param in result.ratfunc.gather_variables():
        model_param = get_matching_model_parameter(model_parameters,
                                                   sf_param.name)
        parameter_mapping[sf_param] = pc.Polynomial(model_param)

    for parameter in result.ratfunc.gather_variables():
        assert parameter in parameter_mapping, repr(
            parameter) + " not in  " + str(parameter_mapping)

    # Convert variables to prophesy variables according to generated mapping
    # Note that the substitution looses the factorization
    state.problem_description.solution_function = pc.substitute_variables_ratfunc(
        result.ratfunc, parameter_mapping)

    state.problem_description.welldefined_constraints = result.welldefined_constraints
    state.problem_description.graph_preserving_constraints = result.graph_preservation_constraints
    if export:
        write_pstorm_result(export, result)
    return state
    def initialize(self, problem_description, constants=None, fixed_threshold = True, fixed_direction = None):
        """
        Initializes the smt solver to consider the problem at hand.

        :param problem_description: 
        :type problem_description: ProblemDescription
        :param threshold:
        """
        if fixed_direction is not None:
            if fixed_direction not in ["safe", "bad"]:
                raise ValueError("Direction can only be fixed to safe or bad")
            self._fixed_direction = fixed_direction
        self.fixed_threshold = fixed_threshold
        lower_bounded_variables = True
        upper_bounded_variables = False
        assert problem_description.solution_function is not None
        assert problem_description.parameters is not None
        if self.fixed_threshold:
            assert problem_description.threshold is not None

        encoding_start = time.time()
        #TODO expanding might be a stupid idea.
        self._ratfunc = pc.expand(problem_description.solution_function)
        self.parameters = problem_description.parameters

        for p in self.parameters:
            self._smt2interface.add_variable(p.name, VariableDomain.Real)

        safeVar = pc.Variable("?_safe", pc.VariableType.BOOL)
        badVar = pc.Variable("?_bad", pc.VariableType.BOOL)
        equalsVar = pc.Variable("?_equals", pc.VariableType.BOOL)
        self._thresholdVar = pc.Variable("T")
        rf1Var = pc.Variable("rf1")
        rf2Var = pc.Variable("rf2")



        self._smt2interface.add_variable(safeVar.name, VariableDomain.Bool)
        self._smt2interface.add_variable(badVar.name, VariableDomain.Bool)
        self._smt2interface.add_variable(equalsVar.name, VariableDomain.Bool)
        self._smt2interface.add_variable(self._thresholdVar.name, VariableDomain.Real)

        #Fix direction after declaring variables
        if self._fixed_direction is not None:
            excluded_dir = "safe" if self._fixed_direction == "bad" else "bad"
            # Notice that we have to flip the values, as we are checking all-quantification
            self._smt2interface.fix_guard("?_" + self._fixed_direction, False)
            self._smt2interface.fix_guard("?_" + excluded_dir, True)
            self._smt2interface.fix_guard("?_equals", False)

        #TODO denominator unequal constant.
        if pc.denominator(self._ratfunc) != 1:
            # We do know now that the well-defined points are connected.
            sample = self._get_welldefined_point(problem_description.welldefined_constraints)
            value = pc.denominator(self._ratfunc).evaluate(sample)
            self._smt2interface.add_variable(rf1Var.name, VariableDomain.Real)
            self._smt2interface.add_variable(rf2Var.name, VariableDomain.Real)
            if upper_bounded_variables and problem_description.property.operator == OperatorType.probability:
                self._smt2interface.assert_constraint(pc.Constraint(pc.Polynomial(rf1Var) - rf2Var, pc.Relation.LESS))
            if value < 0:
                if lower_bounded_variables:
                    self._smt2interface.assert_constraint(pc.Constraint(rf1Var, pc.Relation.LESS, pc.Rational(0)))
                    self._smt2interface.assert_constraint(pc.Constraint(rf2Var, pc.Relation.LESS, pc.Rational(0)))

                safe_constraint = Constraint(pc.Polynomial(rf1Var) - self._thresholdVar * rf2Var, self._bad_relation)
                bad_constraint = Constraint(pc.Polynomial(rf1Var) - self._thresholdVar * rf2Var, self._safe_relation)
                eq_constraint = Constraint(pc.Polynomial(rf1Var) - self._thresholdVar * rf2Var, pc.Relation.EQ)
            else:
                if lower_bounded_variables:
                    self._smt2interface.assert_constraint(pc.Constraint(rf1Var, pc.Relation.GREATER, pc.Rational(0)))
                    self._smt2interface.assert_constraint(pc.Constraint(rf2Var, pc.Relation.GREATER, pc.Rational(0)))

                safe_constraint = Constraint(pc.Polynomial(rf1Var) - self._thresholdVar * rf2Var, self._safe_relation)
                bad_constraint = Constraint(pc.Polynomial(rf1Var) - self._thresholdVar * rf2Var, self._bad_relation)
                eq_constraint = Constraint(pc.Polynomial(rf1Var) - self._thresholdVar * rf2Var, pc.Relation.EQ)
            rf1_constraint = Constraint(pc.Polynomial(rf1Var) - pc.numerator(self._ratfunc), Relation.EQ)
            rf2_constraint = Constraint(pc.Polynomial(rf2Var) - pc.denominator(self._ratfunc), Relation.EQ)
            self._smt2interface.assert_constraint(rf1_constraint)
            self._smt2interface.assert_constraint(rf2_constraint)
        else:
            safe_constraint = Constraint(pc.numerator(self._ratfunc) - pc.Polynomial(self._thresholdVar), self._safe_relation)
            bad_constraint = Constraint(pc.numerator(self._ratfunc) - pc.Polynomial(self._thresholdVar), self._bad_relation)
            eq_constraint = Constraint(pc.numerator(self._ratfunc) - pc.Polynomial(self._thresholdVar), pc.Relation.EQ)

        self._smt2interface.assert_guarded_constraint("?_safe", safe_constraint)
        self._smt2interface.assert_guarded_constraint("?_bad", bad_constraint)
        self._smt2interface.assert_guarded_constraint("?_equals", eq_constraint)

        if self.fixed_threshold:
            self._add_threshold_constraint(problem_description.threshold)
        self._encoding_timer += time.time() - encoding_start
 def _add_threshold_constraint(self, threshold):
     threshold_constraint = pc.Constraint(pc.Polynomial(self._thresholdVar) - threshold,
                                          pc.Relation.EQ)
     self._smt2interface.assert_constraint(threshold_constraint)
Ejemplo n.º 6
0
    def initialize(self,
                   problem_description,
                   fixed_threshold=True,
                   fixed_direction=None):
        """
        
        :param problem_description: 
        :type problem_description: 
        :param constants: 
        :return: 
        """

        if self.fixed_threshold:
            if not problem_description.threshold:
                raise ValueError("ETR with fixed threshold needs a threshold")
            else:
                self._threshold = problem_description.threshold
        if problem_description.model is None:
            raise ValueError(
                "ETR checker requires the model as part of the problem description"
            )
        if fixed_direction is not None:
            if fixed_direction not in ["safe", "bad", "border"]:
                raise ValueError(
                    "Direction can only be fixed to safe, bad, border")
            self._fixed_direction = fixed_direction
        model = self.model_explorer.get_model()
        _property = Property.from_string(
            str(self.model_explorer.pctlformula[0].raw_formula))  #TODO ugly :(

        if model.model_type == sp.ModelType.MDP:
            # TODO for logging smt calls, and obtaining proper stats in the cli, this is not a good idea.
            self._smt2interface_bad = type(self._smt2interface)(
                self._smt2interface.location)
            self._smt2interface_bad.run()
            if _property.operator_direction == OperatorDirection.max:
                self._safe_demonic = True
            else:
                assert _property.operator_direction == OperatorDirection.min
                self._safe_demonic = False

        if len(model.initial_states) > 1:
            raise NotImplementedError(
                "We only support models with a single initial state")
        if len(model.initial_states) == 0:
            raise RuntimeError("We only support models with an initial state.")

        logger.info("Writing equation system to solver")
        self.fixed_threshold = fixed_threshold
        _bounded_variables = True  # Add bounds to all state variables.
        _additional_constraints = self._additional_constraints

        encoding_start = time.time()
        safeVar = pc.Variable("?_safe", pc.VariableType.BOOL)
        badVar = pc.Variable("?_bad", pc.VariableType.BOOL)
        equalsVar = pc.Variable("?_equals", pc.VariableType.BOOL)
        exactVar = pc.Variable("?_exact", pc.VariableType.BOOL)

        self._thresholdVar = pc.Variable("T")

        self.parameters = problem_description.parameters
        for par in self.parameters:
            self._add_variable_to_smtinterfaces(par.name, VariableDomain.Real)

        self._add_variable_to_smtinterfaces(safeVar.name, VariableDomain.Bool)
        self._add_variable_to_smtinterfaces(badVar.name, VariableDomain.Bool)

        self._add_variable_to_smtinterfaces(equalsVar.name,
                                            VariableDomain.Bool)
        self._add_variable_to_smtinterfaces(exactVar.name, VariableDomain.Bool)
        self._add_variable_to_smtinterfaces(self._thresholdVar.name,
                                            VariableDomain.Real)

        if self._fixed_direction is not None:
            #TODO support this for MDPs
            if model.model_type != sp.ModelType.DTMC:
                raise NotImplementedError(
                    "Support for fixed directions and MDPs is not yet present")
            if self._fixed_direction == "border":
                self._smt2interface.fix_guard("?_safe", False)
                self._smt2interface.fix_guard("?_bad", False)
                self._smt2interface.fix_guard("?_equals", True)
            else:
                excluded_dir = "safe" if self._fixed_direction == "bad" else "bad"
                # Notice that we have to flip the values, as we are checking all-quantification
                self._smt2interface.fix_guard("?_" + self._fixed_direction,
                                              False)
                self._smt2interface.fix_guard("?_" + excluded_dir, True)
                self._smt2interface.fix_guard("?_equals", False)

        if self._smt2interface_bad:
            self._smt2interface_bad.fix_guard("?_bad", True)
            self._smt2interface_bad.fix_guard("?_safe", False)
            self._smt2interface_bad.fix_guard("?_equals", False)
            self._smt2interface.fix_guard("?_safe", True)
            self._smt2interface.fix_guard("?_bad", False)
            self._smt2interface.fix_guard("?_equals", False)

        if self._exact:
            self._fix_guard("?_exact", True)
        else:
            raise NotImplementedError(
                "Support for inexact solvign for MDPs is not yet present")

        initial_state_var = None
        self._state_var_mapping = dict()

        if _property.operator == OperatorType.probability:
            prob0, prob1 = self.model_explorer.prob01_states()

            for state in model.states:
                if prob0.get(state.id):
                    continue
                if prob1.get(state.id):
                    continue
                stateVar = pc.Variable("s_" + str(state))
                self._state_var_mapping[state.id] = stateVar
                self._add_variable_to_smtinterfaces(stateVar.name,
                                                    VariableDomain.Real)
                if state.id in model.initial_states:
                    initial_state_var = stateVar
            if initial_state_var is None:
                # TODO
                raise RuntimeError(
                    "Initial state is a prob0/prob1 state. Currently not supported"
                )
        else:
            assert _property.operator == OperatorType.reward
            reward_model = self._get_reward_model(model, _property)

            rew0 = self.model_explorer.rew0_states()
            for state in model.states:
                if rew0.get(state.id):
                    continue
                stateVar = pc.Variable("s_" + str(state))
                self._state_var_mapping[state.id] = stateVar
                self._add_variable_to_smtinterfaces(stateVar.name,
                                                    VariableDomain.Real)
                if state.id in model.initial_states:
                    initial_state_var = stateVar
            if initial_state_var is None:
                raise RuntimeError(
                    "Initial state is a reward 0 state. Currently not supported"
                )

        safe_constraint = pc.Constraint(
            pc.Polynomial(initial_state_var) - self._thresholdVar,
            self._safe_relation)
        bad_constraint = pc.Constraint(
            pc.Polynomial(initial_state_var) - self._thresholdVar,
            self._bad_relation)

        equals_constraint = pc.Constraint(
            pc.Polynomial(initial_state_var) - self._thresholdVar,
            self._equals_relation)
        self._assert_guarded_constraint("?_safe", safe_constraint)
        self._assert_guarded_constraint("?_bad", bad_constraint)
        self._assert_guarded_constraint("?_equals", equals_constraint)
        if self.fixed_threshold:
            self._add_threshold_constraint(self._threshold)

        if problem_description.property.operator == OperatorType.probability:
            for state in model.states:
                state_equations = []
                state_var = self._state_var_mapping.get(state.id)
                if state_var is None:
                    continue
                if _bounded_variables:
                    # if bounded variable constraints are to be added, do so.
                    self._assert_constraint(
                        pc.Constraint(state_var, pc.Relation.GREATER,
                                      pc.Rational(0)))
                    self._assert_constraint(
                        pc.Constraint(state_var, pc.Relation.LESS,
                                      pc.Rational(1)))
                state_equation = -pc.Polynomial(state_var)
                for action in state.actions:
                    action_equation = state_equation
                    for transition in action.transitions:
                        if prob0.get(transition.column):
                            continue
                        # obtain the transition value as a polynomial.
                        if transition.value().is_constant():
                            value = pc.Polynomial(
                                pc.convert_from_storm_type(
                                    transition.value().constant_part()))
                        else:
                            denom = pc.denominator(
                                pc.convert_from_storm_type(transition.value()))
                            if not denom.is_constant():
                                denom = denom.constant_part()

                                value = pc.numerator(
                                    pc.convert_from_storm_type(
                                        transition.value()))
                                value = value.polynomial() * (1 / denom)
                            else:
                                value = pc.expand_from_storm_type(
                                    transition.value())

                        if prob1.get(transition.column):
                            action_equation += value
                            continue
                        action_equation += value * self._state_var_mapping.get(
                            transition.column)
                #logger.debug(state_equation)
                    state_equations.append(action_equation)
                self._add_state_constraint(state_equations, model.model_type,
                                           state.id)
            #Nothing left to be done for the model.
        else:
            for state in model.states:
                state_equations = []
                state_var = self._state_var_mapping.get(state.id)
                if state_var is None:
                    continue
                if _bounded_variables:
                    # if bounded variable constraints are to be added, do so.
                    self._assert_constraint(
                        pc.Constraint(state_var, pc.Relation.GREATER,
                                      pc.Rational(0)))
                state_reward = pc.expand_from_storm_type(
                    reward_model.state_rewards[state.id])
                if state_reward.is_constant():
                    reward_value = state_reward.constant_part()
                else:
                    denom = pc.denominator(state_reward)
                    if denom.is_constant():
                        denom = denom.constant_part()
                        reward_value = pc.numerator(state_reward) * (1 / denom)
                    else:
                        reward_value = state_reward
                state_equation = -pc.Polynomial(state_var) + reward_value

                for action in state.actions:
                    action_equation = state_equation
                    for transition in action.transitions:

                        if rew0.get(transition.column):
                            continue
                        # obtain the transition value as a polynomial.
                        if transition.value().is_constant():
                            value = pc.Polynomial(
                                pc.convert_from_storm_type(
                                    transition.value().constant_part()))
                        else:
                            denom = pc.denominator(
                                pc.convert_from_storm_type(transition.value()))
                            if denom.is_constant():
                                denom = denom.constant_part()

                                value = pc.numerator(
                                    pc.convert_from_storm_type(
                                        transition.value()))
                                value = value.polynomial() * (1 / denom)
                            else:
                                value = pc.expand_from_storm_type(
                                    transition.value())

                        action_equation += value * self._state_var_mapping.get(
                            transition.column)
                #logger.debug(state_equation)
                    state_equations.append(action_equation)

                self._add_state_constraint(state_equations, model.model_type,
                                           state.id)
            #Nothing left to be one

        if _additional_constraints and problem_description.property.operator == OperatorType.probability:
            assert model.model_type == sp.ModelType.DTMC
            for state in model.states:
                state_var = self._state_var_mapping.get(state.id)
                if state_var is None:
                    continue
                state_equation = -pc.Polynomial(state_var)
                for action in state.actions:
                    trans = []
                    for transition in action.transitions:
                        if prob0.get(transition.column):
                            continue
                        # obtain the transition value as a polynomial.
                        if transition.value().is_constant():
                            value = pc.Polynomial(
                                pc.convert_from_storm_type(
                                    transition.value().constant_part()))
                        else:
                            denom = pc.denominator(
                                pc.convert_from_storm_type(transition.value()))
                            if not denom.is_constant():
                                raise RuntimeError(
                                    "only polynomial constraints are supported right now."
                                )
                            denom = denom.constant_part()

                            value = pc.numerator(
                                pc.convert_from_storm_type(transition.value()))
                            value = value.polynomial() * (1 / denom)

                        if prob1.get(transition.column):
                            trans.append((None, value))
                        else:
                            trans.append((self._state_var_mapping.get(
                                transition.column), value))

                    prob0, prob1 = self.model_explorer.prob01_states()

                    if len(trans) == 1:
                        if trans[0][0] and trans[0][1] != 1:
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][0],
                                    pc.Relation.LESS))
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][1],
                                    pc.Relation.LESS))
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][0] -
                                    trans[0][1] + pc.Rational(1),
                                    pc.Relation.GREATER))
                    if len(trans) == 2:
                        if trans[0][0] and trans[0][1] != 1 and trans[1][
                                0] and trans[1][1] != 1:
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][0] -
                                    trans[1][0], pc.Relation.LESS),
                                name="app2ubstst{}".format(state.id))
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][0] -
                                    trans[1][1], pc.Relation.LESS),
                                name="app2ubsttr{}".format(state.id))
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][1] -
                                    trans[1][0], pc.Relation.LESS),
                                name="app2ubtrst{}".format(state.id))
                            self._smt2interface.assert_constraint(
                                pc.Constraint(
                                    pc.Polynomial(state_var) - trans[0][0] -
                                    trans[1][0] + pc.Rational(1),
                                    pc.Relation.GREATER),
                                name="app2lbsttr{}".format(state.id))

                    #Nothing left to be done for the model.

        self._encoding_timer += time.time() - encoding_start
def read_pstorm_result(location, require_result=True):
    """
    Read the output of pstorm into a ParametricResult
    :param location: The location of the file to be read
    :type location: str
    :param require_result: If true, parsing fails if no result is found
    :type require_result: Bool
    :return: The data obtained from the model.
    :rtype: ParametricResult
    """
    logging.debug("Open result file from storm...")
    with open(location) as f:
        inputstring = f.read()

    # Build parameters
    logger.debug("Reading parameters...")
    parameters = ParameterOrder()
    parameter_strings = re.findall(r'\$Parameters:\s(.*)',
                                   inputstring)[0].split(";")
    for parameter_string in parameter_strings:
        if parameter_string.strip():
            name_and_info = parameter_string.split()
            var = pc.variable_with_name(name_and_info[0].strip())
            if var.is_no_variable:
                var = pc.Variable(name_and_info[0].strip())
            if len(name_and_info) == 1:
                bound = interval.Interval(pc.Rational(0),
                                          interval.BoundType.open,
                                          pc.Rational(1),
                                          interval.BoundType.open)
            else:
                bound = interval.string_to_interval(name_and_info[1],
                                                    pc.Rational)
            parameters.append(Parameter(var, bound))
    logger.debug("Parameters: %s", str(parameters))

    # Build well-defined constraints
    logging.debug("Reading constraints...")
    constraints_string = re.findall(
        r'(\$Well-formed Constraints:\s*\n.+?)(?=\$|(?:\s*\Z))', inputstring,
        re.DOTALL)[0]
    constraints_string = constraints_string.split("\n")[:-1]
    constraints = [pc.parse(cond) for cond in constraints_string[1:]]
    logger.debug("Constraints: %s", ",".join([str(c) for c in constraints]))
    # Build graph-preserving constraints
    constraints_string = re.findall(
        r'(\$Graph-preserving Constraints:\s*\n.+?)(?=\$|(?:\s*\Z))',
        inputstring, re.DOTALL)[0]
    constraints_string = constraints_string.split("\n")
    gpconstraints = [
        pc.parse(cond) for cond in constraints_string[1:] if cond.strip() != ""
    ]
    logger.debug("GP Constraints: %s",
                 ",".join([str(c) for c in gpconstraints]))

    # Build rational function
    logger.debug("Looking for solution function...")
    match = re.findall(r'\$Result:(.*)$', inputstring, re.MULTILINE)

    if require_result:
        if len(match) == 0:
            raise ValueError(
                "Expected a result in the result file at {}".format(location))
    if len(match) > 0:
        logger.debug("Building solution function...")
        solution = pc.parse(match[0])
        if isinstance(solution, pc.Monomial):
            solution = pc.Polynomial(solution)
        logger.debug("Solution function is %s", solution)
    else:
        solution = None

    logger.debug("Parsing complete.")
    return ParametricResult(parameters, constraints, gpconstraints, solution)