예제 #1
0
def test_constants_parsing():
    x_var = pc.Variable("x")
    y_var = pc.Variable("y")
    constants = parse_constants_string("x=y,y=2")
    assert x_var in constants
    assert y_var in constants
    constants = parse_constants_string("z=y,a=2")
    z_var = pc.variable_with_name("z")
    assert z_var in constants
    assert "a=2" in constants.to_key_value_string()
    assert "z=y" in constants.to_key_value_string()
def read_param_result(location):
    with open(location) as f:
        inputs = [l.strip() for l in f.readlines()]

    # Build parameters
    logger.debug("Reading parameters")
    parameters = ParameterOrder()
    parameter_strings = inputs[1][1:-1].split(", ")
    for parameter_string in parameter_strings:
        if parameter_string.strip():
            var = pc.Variable(parameter_string.strip().strip())
            bound = interval.Interval(0.0, interval.BoundType.open, 1.0,
                                      interval.BoundType.open)
            parameters.append(Parameter(var, bound))

    logger.debug("Reading constraints")
    ranges = re.split(r"(?<=]) (?=\[)", inputs[2][1:-1])
    ranges = [r[1:-1].split(", ") for r in ranges]
    if len(parameter_strings) != len(ranges):
        raise RuntimeError(
            "Number of ranges does not match number of parameters")
    # Build well-defined constraints
    constraints = []
    for (p, ran) in zip(parameters, ranges):
        # ran = [lower .. upper]
        constraints.append(Constraint(p.variable - ran[0], Relation.GEQ))
        constraints.append(Constraint(p.variable - ran[1], Relation.LEQ))

    # Build rational function
    logger.debug("Parsing rational function")
    ratfunc = pc.RationalFunction(parse(inputs[3]))

    return ParametricResult(parameters, constraints, ratfunc)
예제 #3
0
def parse_constants_string(input_string):
    """
    Parses a key-value string 
    
    :param input_string: 
    :return: 
    """
    result = Constants()
    if input_string is None or input_string == "":
        return result
    defs = input_string.split(",")
    for constant_def in defs:
        constant_and_def = constant_def.split("=")
        if len(constant_and_def) != 2:
            raise ValueError(
                "Expected key-value pair, found {}".format(constant_def))
        var = pc.variable_with_name(constant_and_def[0])
        if var.is_no_variable:
            var = pc.Variable(constant_and_def[0])
        val = str(constant_and_def[1])
        result[var] = val
    return result
예제 #4
0
def test_region_string():
    h2 = HyperRectangle(string_to_interval("[2,5]", pc.Rational),
                        string_to_interval("[3,6]", pc.Rational))
    variables = [pc.Variable("x"), pc.Variable("y")]
    h3 = HyperRectangle.from_region_string(h2.to_region_string(variables),
                                           variables)
    assert h2 == h3
    h4 = HyperRectangle(string_to_interval("(2,5]", pc.Rational),
                        string_to_interval("[3,6]", pc.Rational))
    variables = [pc.Variable("x"), pc.Variable("y")]
    h5 = HyperRectangle.from_region_string(h4.to_region_string(variables),
                                           variables)
    assert h4 == h5
    assert h4 != h3
    h6 = HyperRectangle(string_to_interval("[2,5)", pc.Rational),
                        string_to_interval("(3,6)", pc.Rational))
    variables = [pc.Variable("x"), pc.Variable("y")]
    h7 = HyperRectangle.from_region_string(h6.to_region_string(variables),
                                           variables)
    assert h6 == h7
    assert h6 != h3
    assert h6 != h5
    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
예제 #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
예제 #7
0
def test_contains():
    pycarl.clear_variable_pool()
    x = pc.Variable("x")
    p = Parameter(x, string_to_interval("(0,1)", int))
    p_new = pickle.loads(pickle.dumps(p))
    assert p == p_new
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)