From 68b2b7166b3b0c01cd443f68837f0242513bc07a Mon Sep 17 00:00:00 2001 From: sjunges Date: Mon, 18 Dec 2017 22:23:08 +0100 Subject: [PATCH 001/147] bindings for expression type --- src/storage/expressions.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index a12cdc9..f65d16f 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -38,4 +38,10 @@ void define_expressions(py::module& m) { .def("__str__", &streamToString) ; + py::class_(m, "ExpressionType", "The type of an expression") + .def_property_readonly("is_boolean", &storm::expressions::Type::isBooleanType) + .def_property_readonly("is_integer", &storm::expressions::Type::isIntegerType) + .def_property_readonly("is_rational", &storm::expressions::Type::isRationalType) + .def("__str__", &storm::expressions::Type::getStringRepresentation); + } \ No newline at end of file From 9e42f73015d4bc1e34dcd0f4c2cea0bf420640c0 Mon Sep 17 00:00:00 2001 From: sjunges Date: Mon, 18 Dec 2017 22:23:45 +0100 Subject: [PATCH 002/147] towards support for more info from prism programs --- src/core/input.cpp | 19 ------------------- src/mod_storage.cpp | 2 ++ src/storage/prism.cpp | 33 +++++++++++++++++++++++++++++++++ src/storage/prism.h | 5 +++++ 4 files changed, 40 insertions(+), 19 deletions(-) create mode 100644 src/storage/prism.cpp create mode 100644 src/storage/prism.h diff --git a/src/core/input.cpp b/src/core/input.cpp index a87c18a..1ce6a5a 100644 --- a/src/core/input.cpp +++ b/src/core/input.cpp @@ -19,25 +19,6 @@ void define_input(py::module& m) { // Parse Jani model m.def("parse_jani_model", &storm::api::parseJaniModel, "Parse Jani model", py::arg("path")); - // PrismType - py::enum_(m, "PrismModelType", "Type of the prism model") - .value("DTMC", storm::prism::Program::ModelType::DTMC) - .value("CTMC", storm::prism::Program::ModelType::CTMC) - .value("MDP", storm::prism::Program::ModelType::MDP) - .value("CTMDP", storm::prism::Program::ModelType::CTMDP) - .value("MA", storm::prism::Program::ModelType::MA) - .value("UNDEFINED", storm::prism::Program::ModelType::UNDEFINED) - ; - - // PrismProgram - py::class_(m, "PrismProgram", "Prism program") - .def_property_readonly("nr_modules", &storm::prism::Program::getNumberOfModules, "Number of modules") - .def_property_readonly("model_type", &storm::prism::Program::getModelType, "Model type") - .def_property_readonly("has_undefined_constants", &storm::prism::Program::hasUndefinedConstants, "Flag if program has undefined constants") - .def_property_readonly("undefined_constants_are_graph_preserving", &storm::prism::Program::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") - .def("__str__", &streamToString) - ; - // JaniType py::enum_(m, "JaniModelType", "Type of the Jani model") .value("DTMC", storm::jani::ModelType::DTMC) diff --git a/src/mod_storage.cpp b/src/mod_storage.cpp index b507db6..62ced7d 100644 --- a/src/mod_storage.cpp +++ b/src/mod_storage.cpp @@ -3,6 +3,7 @@ #include "storage/bitvector.h" #include "storage/model.h" #include "storage/matrix.h" +#include "storage/prism.h" #include "storage/state.h" #include "storage/labeling.h" #include "storage/expressions.h" @@ -19,6 +20,7 @@ PYBIND11_MODULE(storage, m) { define_model(m); define_sparse_matrix(m); define_state(m); + define_prism(m); define_labeling(m); define_expressions(m); } diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp new file mode 100644 index 0000000..4c97de3 --- /dev/null +++ b/src/storage/prism.cpp @@ -0,0 +1,33 @@ +#include "prism.h" +#include +#include "src/helpers.h" + +using namespace storm::prism; + +void define_prism(py::module& m) { + py::class_> program(m, "PrismProgram", "A Prism Program"); + program.def_property_readonly("constants", &Program::getConstants, "Get Program Constants") + .def_property_readonly("nr_modules", &storm::prism::Program::getNumberOfModules, "Number of modules") + .def_property_readonly("model_type", &storm::prism::Program::getModelType, "Model type") + .def_property_readonly("has_undefined_constants", &storm::prism::Program::hasUndefinedConstants, "Flag if program has undefined constants") + .def_property_readonly("undefined_constants_are_graph_preserving", &storm::prism::Program::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") + .def("__str__", &streamToString); + + + // PrismType + py::enum_(m, "PrismModelType", "Type of the prism model") + .value("DTMC", storm::prism::Program::ModelType::DTMC) + .value("CTMC", storm::prism::Program::ModelType::CTMC) + .value("MDP", storm::prism::Program::ModelType::MDP) + .value("CTMDP", storm::prism::Program::ModelType::CTMDP) + .value("MA", storm::prism::Program::ModelType::MA) + .value("UNDEFINED", storm::prism::Program::ModelType::UNDEFINED) + ; + + + py::class_> constant(m, "Constant", "A constant in a Prism program"); + constant.def_property_readonly("name", &Constant::getName, "Constant name") + .def_property_readonly("defined", &Constant::isDefined, "Is the constant defined?") + .def_property_readonly("type", &Constant::getType, "The type of the constant"); + +} \ No newline at end of file diff --git a/src/storage/prism.h b/src/storage/prism.h new file mode 100644 index 0000000..17f3a63 --- /dev/null +++ b/src/storage/prism.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +void define_prism(py::module& m); From cb9145ca6a783e412aa70522c3048c79b3eeab6a Mon Sep 17 00:00:00 2001 From: sjunges Date: Tue, 19 Dec 2017 08:56:55 +0100 Subject: [PATCH 003/147] example --- .../highlevel_models/01-highlevel-models.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 examples/highlevel_models/01-highlevel-models.py diff --git a/examples/highlevel_models/01-highlevel-models.py b/examples/highlevel_models/01-highlevel-models.py new file mode 100644 index 0000000..46e2f41 --- /dev/null +++ b/examples/highlevel_models/01-highlevel-models.py @@ -0,0 +1,17 @@ +import stormpy +import stormpy.core + +import stormpy.examples +import stormpy.examples.files + + +def example_highlevel_models(): + path = stormpy.examples.files.prism_pdtmc_die + prism_program = stormpy.parse_prism_program(path) + for c in prism_program.constants: + print("constant {} with type {} is {} defined".format(c.name, c.type, "" if c.defined else "not")) + + + +if __name__ == '__main__': + example_highlevel_models() \ No newline at end of file From 5c1f29bca6058570d2f311afddd18fe03f070444 Mon Sep 17 00:00:00 2001 From: sjunges Date: Tue, 19 Dec 2017 08:57:10 +0100 Subject: [PATCH 004/147] expression handling (wip) --- src/storage/expressions.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index f65d16f..1a4db7b 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -3,6 +3,7 @@ #include "storm/storage/expressions/ExpressionManager.h" #include "storm/storage/expressions/Expression.h" +#include "storm/parser/ExpressionParser.h" //Define python bindings void define_expressions(py::module& m) { @@ -38,6 +39,10 @@ void define_expressions(py::module& m) { .def("__str__", &streamToString) ; + py::class_(m, "ExpressionParser", "Parser for storm-expressions") + .def(py::init(), "Expression Manager to use", py::arg("expression_manager")) + .def("parse", &storm::parser::ExpressionParser::parseFromString, "parse"); + py::class_(m, "ExpressionType", "The type of an expression") .def_property_readonly("is_boolean", &storm::expressions::Type::isBooleanType) .def_property_readonly("is_integer", &storm::expressions::Type::isIntegerType) From a2f23e38d49d8263d56288e12f328dba838ac0ad Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 20 Dec 2017 18:29:44 +0100 Subject: [PATCH 005/147] QualitatitiveCheckResult, convenient at. --- src/core/result.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/result.cpp b/src/core/result.cpp index 6d7d9ab..55a62a2 100644 --- a/src/core/result.cpp +++ b/src/core/result.cpp @@ -43,7 +43,10 @@ void define_result(py::module& m) { // QualitativeCheckResult py::class_> qualitativeCheckResult(m, "_QualitativeCheckResult", "Abstract class for qualitative model checking results", checkResult); py::class_>(m, "ExplicitQualitativeCheckResult", "Explicit qualitative model checking result", qualitativeCheckResult) - .def("get_truth_values", &storm::modelchecker::ExplicitQualitativeCheckResult::getTruthValuesVector, "Get BitVector representing the truth values") + .def("at", [](storm::modelchecker::ExplicitQualitativeCheckResult const& result, storm::storage::sparse::state_type state) { + return result[state]; + }, py::arg("state"), "Get result for given state") + .def("get_truth_values", &storm::modelchecker::ExplicitQualitativeCheckResult::getTruthValuesVector, "Get BitVector representing the truth values") ; // QuantitativeCheckResult From f98575d82ce361a663e60f188cc1f894941c8f17 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 20 Dec 2017 18:33:00 +0100 Subject: [PATCH 006/147] ExpressionParser --- src/storage/expressions.cpp | 7 +++++-- tests/storage/test_expressions.py | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index 1a4db7b..87613f4 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -36,12 +36,15 @@ void define_expressions(py::module& m) { .def("has_boolean_type", &storm::expressions::Expression::hasBooleanType, "Check if the expression is a boolean") .def("has_integer_type", &storm::expressions::Expression::hasIntegerType, "Check if the expression is an integer") .def("has_rational_type", &storm::expressions::Expression::hasRationalType, "Check if the expression is a rational") - .def("__str__", &streamToString) + .def_property_readonly("type", &storm::expressions::Expression::getType, "Get the Type") + .def("__str__", &storm::expressions::Expression::toString, "To string") ; py::class_(m, "ExpressionParser", "Parser for storm-expressions") .def(py::init(), "Expression Manager to use", py::arg("expression_manager")) - .def("parse", &storm::parser::ExpressionParser::parseFromString, "parse"); + .def("set_identifier_mapping", [](storm::parser::ExpressionParser& p, std::unordered_map const& identifierMapping) {p.setIdentifierMapping(identifierMapping);}, "sets identifiers") + .def("parse", &storm::parser::ExpressionParser::parseFromString, "parse") + ; py::class_(m, "ExpressionType", "The type of an expression") .def_property_readonly("is_boolean", &storm::expressions::Type::isBooleanType) diff --git a/tests/storage/test_expressions.py b/tests/storage/test_expressions.py index c78421c..0e82122 100644 --- a/tests/storage/test_expressions.py +++ b/tests/storage/test_expressions.py @@ -31,3 +31,11 @@ class TestExpressions: assert not expression.has_boolean_type() assert not expression.has_integer_type() assert expression.has_rational_type() + + def test_expression_parser(self): + manager = stormpy.ExpressionManager() + ep = stormpy.ExpressionParser(manager) + ep.set_identifier_mapping(dict()) + print(ep.parse("true")) + print(ep.parse("1.0")) + assert ep.parse("1.0").has_rational_type() From b9c5ff5a63a3d2bf78adfe1db2e7ec2cda29c884 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 20 Dec 2017 18:33:17 +0100 Subject: [PATCH 007/147] SubstituteConstants in PrismProgram --- src/storage/prism.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index 4c97de3..b982bd0 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -1,6 +1,7 @@ #include "prism.h" #include #include "src/helpers.h" +#include using namespace storm::prism; @@ -11,6 +12,9 @@ void define_prism(py::module& m) { .def_property_readonly("model_type", &storm::prism::Program::getModelType, "Model type") .def_property_readonly("has_undefined_constants", &storm::prism::Program::hasUndefinedConstants, "Flag if program has undefined constants") .def_property_readonly("undefined_constants_are_graph_preserving", &storm::prism::Program::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") + .def("substitute_constants", &Program::substituteConstants, "Substitute constants within program") + .def("define_constants", &Program::defineUndefinedConstants, "Define constants") + .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") .def("__str__", &streamToString); @@ -28,6 +32,8 @@ void define_prism(py::module& m) { py::class_> constant(m, "Constant", "A constant in a Prism program"); constant.def_property_readonly("name", &Constant::getName, "Constant name") .def_property_readonly("defined", &Constant::isDefined, "Is the constant defined?") - .def_property_readonly("type", &Constant::getType, "The type of the constant"); + .def_property_readonly("type", &Constant::getType, "The type of the constant") + .def_property_readonly("variable", &Constant::getExpressionVariable, "Expression variable") + ; } \ No newline at end of file From 0248454856062822fe01768bd3604607cdd74c25 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 20 Dec 2017 21:39:28 +0100 Subject: [PATCH 008/147] exploring prism programs --- src/storage/prism.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index b982bd0..307ce94 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -9,6 +9,7 @@ void define_prism(py::module& m) { py::class_> program(m, "PrismProgram", "A Prism Program"); program.def_property_readonly("constants", &Program::getConstants, "Get Program Constants") .def_property_readonly("nr_modules", &storm::prism::Program::getNumberOfModules, "Number of modules") + .def_property_readonly("modules", &storm::prism::Program::getModules, "Modules in the program") .def_property_readonly("model_type", &storm::prism::Program::getModelType, "Model type") .def_property_readonly("has_undefined_constants", &storm::prism::Program::hasUndefinedConstants, "Flag if program has undefined constants") .def_property_readonly("undefined_constants_are_graph_preserving", &storm::prism::Program::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") @@ -17,6 +18,23 @@ void define_prism(py::module& m) { .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") .def("__str__", &streamToString); + py::class_ module(m, "PrismModule", "A module in a Prism program"); + module.def_property_readonly("commands", [](Module const& module) {return module.getCommands();}, "Commands in the module") + .def_property_readonly("name", &Module::getName, "Name of the module") + ; + + py::class_ command(m, "PrismCommand", "A command in a Prism program"); + command.def_property_readonly("global_index", &Command::getGlobalIndex, "Get global index") + .def_property_readonly("guard_expression", &Command::getGuardExpression, "Get guard expression") + .def_property_readonly("updates", &Command::getUpdates, "Updates in the command"); + + py::class_ update(m, "PrismUpdate", "An update in a Prism command"); + update.def_property_readonly("assignments", &Update::getAssignments, "Assignments in the update"); + + py::class_ assignment(m, "PrismAssignment", "An assignment in prism"); + assignment.def_property_readonly("variable", &Assignment::getVariable, "Variable that is updated") + .def_property_readonly("expression", &Assignment::getExpression, "Expression for the update"); + // PrismType py::enum_(m, "PrismModelType", "Type of the prism model") From 703034660fc3125f6a9910bb10eba26d1a6750df Mon Sep 17 00:00:00 2001 From: sjunges Date: Thu, 21 Dec 2017 00:39:14 +0100 Subject: [PATCH 009/147] high level counterexamples --- src/core/counterexample.cpp | 16 ++++++++++++++++ src/core/counterexample.h | 5 +++++ src/mod_core.cpp | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 src/core/counterexample.cpp create mode 100644 src/core/counterexample.h diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp new file mode 100644 index 0000000..9676790 --- /dev/null +++ b/src/core/counterexample.cpp @@ -0,0 +1,16 @@ +#include "counterexample.h" +#include "storm/environment/Environment.h" + +using namespace storm::counterexamples; + +// Define python bindings +void define_counterexamples(py::module& m) { + py::class_>(m, "FlatSet", "Container to pass to program"); + + + py::class_>(m, "SMTCounterExampleGenerator", "Highlevel Counterexample Generator with SMT as backend"). + def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleCommandSet) + ; + + +} diff --git a/src/core/counterexample.h b/src/core/counterexample.h new file mode 100644 index 0000000..aa82a95 --- /dev/null +++ b/src/core/counterexample.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +void define_counterexamples(py::module& m); diff --git a/src/mod_core.cpp b/src/mod_core.cpp index 583eff6..f7f1140 100644 --- a/src/mod_core.cpp +++ b/src/mod_core.cpp @@ -6,6 +6,7 @@ #include "core/bisimulation.h" #include "core/input.h" #include "core/analysis.h" +#include "core/counterexample.h" #include "core/environment.h" PYBIND11_MODULE(core, m) { @@ -23,6 +24,7 @@ PYBIND11_MODULE(core, m) { define_export(m); define_result(m); define_modelchecking(m); + define_counterexamples(m); define_bisimulation(m); define_input(m); define_graph_constraints(m); From 10eccdced3f716a01b5975339b51eefbf4037450 Mon Sep 17 00:00:00 2001 From: sjunges Date: Thu, 21 Dec 2017 00:39:36 +0100 Subject: [PATCH 010/147] builder options wrapped --- src/core/core.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/core/core.cpp b/src/core/core.cpp index d5f03be..63a6107 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -58,14 +58,27 @@ std::shared_ptr buildSparseModel(storm::storage::Symbo } } +// Thin wrapper for model building using one formula as argument +template +std::shared_ptr buildSparseModelWithOptions(storm::storage::SymbolicModelDescription const& modelDescription, storm::builder::BuilderOptions const& options, bool jit = false, bool doctor = false) { + return storm::api::buildSparseModel(modelDescription, options, jit, doctor); + +} + void define_build(py::module& m) { // Build model m.def("_build_sparse_model_from_prism_program", &buildSparseModel, "Build the model", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); m.def("_build_sparse_parametric_model_from_prism_program", &buildSparseModel, "Build the parametric model", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); + m.def("build_sparse_model_with_options", &buildSparseModelWithOptions, "Build the model", py::arg("model_description"), py::arg("options"), py::arg("use_jit") = false, py::arg("doctor") = false); m.def("_build_sparse_model_from_drn", &storm::api::buildExplicitDRNModel, "Build the model from DRN", py::arg("file")); m.def("_build_sparse_parametric_model_from_drn", &storm::api::buildExplicitDRNModel, "Build the parametric model from DRN", py::arg("file")); m.def("build_sparse_model_from_explicit", &storm::api::buildExplicitModel, "Build the model model from explicit input", py::arg("transition_file"), py::arg("labeling_file"), py::arg("state_reward_file") = "", py::arg("transition_reward_file") = "", py::arg("choice_labeling_file") = ""); + + py::class_(m, "BuilderOptions", "Options for building process") + .def(py::init> const&>(), "Initialise with formulae to preserve", py::arg("formulae")) + .def(py::init(), "Initialise without formulae", py::arg("build_all_reward_models"), py::arg("build_all_labels")) + .def("set_build_with_choice_origins", &storm::builder::BuilderOptions::setBuildChoiceOrigins, "Build choice origins", py::arg("new_value")); } // Thin wrapper for exporting model From 5873ac24b2d38d832c81c300efc9712e41177962 Mon Sep 17 00:00:00 2001 From: sjunges Date: Thu, 21 Dec 2017 00:40:10 +0100 Subject: [PATCH 011/147] more prismprogram functionality --- src/storage/prism.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index 307ce94..508e2c6 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -15,6 +15,9 @@ void define_prism(py::module& m) { .def_property_readonly("undefined_constants_are_graph_preserving", &storm::prism::Program::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") .def("substitute_constants", &Program::substituteConstants, "Substitute constants within program") .def("define_constants", &Program::defineUndefinedConstants, "Define constants") + .def("restrict_commands", &Program::restrictCommands, "Restrict commands") + .def("simplify", &Program::simplify, "Simplify") + .def("used_constants",&Program::usedConstants, "Compute Used Constants") .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") .def("__str__", &streamToString); From 2cedd7fe1e375c958e8301fa875e581a9701fd60 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 28 Mar 2018 18:53:05 +0200 Subject: [PATCH 012/147] counterexamples updated to reflect code change in storm --- src/core/counterexample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index 9676790..6fbb6a1 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -9,7 +9,7 @@ void define_counterexamples(py::module& m) { py::class_>(m, "SMTCounterExampleGenerator", "Highlevel Counterexample Generator with SMT as backend"). - def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleCommandSet) + def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleLabelSet) ; From 166cae849956e9976143218122ea77bb3f534fb1 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 2 Apr 2018 17:23:34 +0200 Subject: [PATCH 013/147] cloning formulae --- src/logic/formulae.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index d6d2dd8..5b73050 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -1,5 +1,6 @@ #include "formulae.h" #include "storm/logic/Formulas.h" +#include "storm/logic/CloneVisitor.h" void define_formulae(py::module& m) { @@ -13,6 +14,7 @@ void define_formulae(py::module& m) { py::class_> formula(m, "Formula", "Generic Storm Formula"); formula.def("__str__", &storm::logic::Formula::toString) + .def("clone", [](storm::logic::Formula const& f) { storm::logic::CloneVisitor cv; return cv.clone(f);}) ; // Path Formulae From 3332c66b2c0f9d74a16cc11e8dcc42a058259bd6 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 2 Apr 2018 17:24:11 +0200 Subject: [PATCH 014/147] contains variable --- src/storage/expressions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index 8f9dc34..3e564ab 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -32,6 +32,7 @@ void define_expressions(py::module& m) { // Expression py::class_>(m, "Expression", "Holds an expression") .def("contains_variables", &storm::expressions::Expression::containsVariables, "Check if the expression contains variables.") + .def("contains_variable", &storm::expressions::Expression::containsVariable, "Check if the expression contains any of the given variables.", py::arg("variables")) .def("is_literal", &storm::expressions::Expression::isLiteral, "Check if the expression is a literal") .def("has_boolean_type", &storm::expressions::Expression::hasBooleanType, "Check if the expression is a boolean") .def("has_integer_type", &storm::expressions::Expression::hasIntegerType, "Check if the expression is an integer") From e7e474bebcc732e27d85a92721f8c7abf8e69e4c Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 2 Apr 2018 17:25:00 +0200 Subject: [PATCH 015/147] support for JANI --- src/core/core.cpp | 2 ++ src/core/input.cpp | 9 +------ src/mod_storage.cpp | 2 ++ src/storage/jani.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++ src/storage/jani.h | 5 ++++ src/storage/prism.cpp | 3 ++- 6 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 src/storage/jani.cpp create mode 100644 src/storage/jani.h diff --git a/src/core/core.cpp b/src/core/core.cpp index d32af44..dddb7d6 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -35,6 +35,8 @@ void define_parse(py::module& m) { :return: A list of properties )dox", py::arg("formula_string"), py::arg("prism_program"), py::arg("property_filter") = boost::none); + m.def("parse_properties_for_jani_model", &storm::api::parsePropertiesForJaniModel, py::arg("formula_string"), py::arg("jani_model"), py::arg("property_filter") = boost::none); + // Pair py::class_(m, "ModelFormulasPair", "Pair of model and formulas") .def_property_readonly("model", [](storm::storage::ModelFormulasPair const& pair) { diff --git a/src/core/input.cpp b/src/core/input.cpp index 1ce6a5a..29c7c3e 100644 --- a/src/core/input.cpp +++ b/src/core/input.cpp @@ -4,6 +4,7 @@ void define_property(py::module& m) { py::class_(m, "Property", "Property") .def(py::init const&, std::string const&>(), "Construct property from formula", py::arg("name"), py::arg("formula"), py::arg("comment") = "") + .def(py::init()) .def_property_readonly("name", &storm::jani::Property::getName, "Obtain the name of the property") .def_property_readonly("raw_formula", &storm::jani::Property::getRawFormula, "Obtain the formula directly") .def("__str__", &streamToString) @@ -36,14 +37,6 @@ void define_input(py::module& m) { .value("UNDEFINED", storm::jani::ModelType::UNDEFINED) ; - // Jani Model - py::class_(m, "JaniModel", "Jani Model") - .def_property_readonly("name", &storm::jani::Model::getName, "Name of the jani model") - .def_property_readonly("model_type", &storm::jani::Model::getModelType, "Model type") - .def_property_readonly("has_undefined_constants", &storm::jani::Model::hasUndefinedConstants, "Flag if Jani model has undefined constants") - .def_property_readonly("undefined_constants_are_graph_preserving", &storm::jani::Model::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") - ; - // SymbolicModelDescription py::class_(m, "SymbolicModelDescription", "Symbolic description of model") .def(py::init(), "Construct from Prism program", py::arg("prism_program")) diff --git a/src/mod_storage.cpp b/src/mod_storage.cpp index 4fd3cbd..28a328e 100644 --- a/src/mod_storage.cpp +++ b/src/mod_storage.cpp @@ -6,6 +6,7 @@ #include "storage/distribution.h" #include "storage/scheduler.h" #include "storage/prism.h" +#include "storage/jani.h" #include "storage/state.h" #include "storage/labeling.h" #include "storage/expressions.h" @@ -23,6 +24,7 @@ PYBIND11_MODULE(storage, m) { define_sparse_matrix(m); define_state(m); define_prism(m); + define_jani(m); define_labeling(m); define_expressions(m); define_scheduler(m, "Double"); diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp new file mode 100644 index 0000000..3574a35 --- /dev/null +++ b/src/storage/jani.cpp @@ -0,0 +1,60 @@ +#include "jani.h" +#include +#include +#include "src/helpers.h" + +using namespace storm::jani; + +std::string janiToString(Model const& m) { + std::stringstream str; + JsonExporter::toStream(m, {}, str); + return str.str(); +} + +void define_jani(py::module& m) { + py::class_> md(m, "JaniModel", "A Jani Model"); + md.def_property_readonly("name", &Model::getName, "model name") + .def_property_readonly("automata", [](const Model& model) {return model.getAutomata();}, "get automata") + .def_property_readonly("constants", [](const Model& model) {return model.getConstants();}, "get constants") + .def("restrict_edges", &Model::restrictEdges, "restrict model to edges given by set", py::arg("edge_set")) + .def("__str__", &janiToString) + .def("get_automaton_index", &Model::getAutomatonIndex, "get index for automaton name") + .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") + .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") + ; + py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); + automaton.def_property_readonly("edges",[](const Automaton& a) {return a.getEdges();}, "get edges") + .def_property_readonly("name", &Automaton::getName); + py::class_> edge(m, "JaniEdge", "A Jani Edge"); + edge.def_property_readonly("source_location_index", &Edge::getSourceLocationIndex, "index for source location") + .def_property_readonly("destinations", &Edge::getDestinations, "edge destinations") + .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") + .def_property_readonly("guard", &Edge::getGuard, "edge guard") + .def("substitute", &Edge::substitute, py::arg("mapping")) + .def("has_silent_action", &Edge::hasSilentAction, "Is the edge labelled with the silent action"); + py::class_> templateEdge(m, "JaniTemplateEdge", "Template edge, internal data structure for edges"); + templateEdge.def(py::init()); + + py::class_> edgeDestination(m, "JaniEdgeDestination", "Destination in Jani"); + edgeDestination.def_property_readonly("target_location_index", &EdgeDestination::getLocationIndex) + .def_property_readonly("probability", &EdgeDestination::getProbability) + .def_property_readonly("assignments", &EdgeDestination::getOrderedAssignments) + ; + py::class_> orderedAssignments(m, "JaniOrderedAssignments", "Set of assignments"); + orderedAssignments.def("__iter__", [](OrderedAssignments &v) { + return py::make_iterator(v.begin(), v.end()); + }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ + .def("__str__", &streamToString) + ; + py::class_> assignment(m, "JaniAssignment", "Jani Assignment"); + assignment.def("__str__", &streamToString) + .def_property("expression", &Assignment::getAssignedExpression, &Assignment::setAssignedExpression) + ; + py::class_> constant(m, "JaniConstant", "A Constant in JANI"); + constant.def_property_readonly("defined", &Constant::isDefined, "is constant defined by some expression") + .def_property_readonly("name", &Constant::getName, "name of constant") + .def_property_readonly("type", &Constant::getType, "type of constant") + .def_property_readonly("expression_variable", &Constant::getExpressionVariable, "expression variable for this constant"); + + +} \ No newline at end of file diff --git a/src/storage/jani.h b/src/storage/jani.h new file mode 100644 index 0000000..27d5cef --- /dev/null +++ b/src/storage/jani.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +void define_jani(py::module& m); diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index c011fcc..01338ea 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -2,7 +2,7 @@ #include #include "src/helpers.h" #include - +#include using namespace storm::prism; @@ -20,6 +20,7 @@ void define_prism(py::module& m) { .def("simplify", &Program::simplify, "Simplify") .def("used_constants",&Program::usedConstants, "Compute Used Constants") .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") + .def("to_jani", &Program::toJani, "Transform to Jani program", py::arg("all_variables_global")=false) .def("__str__", &streamToString); py::class_ module(m, "PrismModule", "A module in a Prism program"); From 717fa454d2e479318ae0e7b2bd37b497c24949b7 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 18 Apr 2018 15:15:39 +0200 Subject: [PATCH 016/147] Updated example drn file --- lib/stormpy/examples/files/ctmc/dft.drn | 90 +++++++++++++------------ tests/core/test_modelchecking.py | 2 +- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/lib/stormpy/examples/files/ctmc/dft.drn b/lib/stormpy/examples/files/ctmc/dft.drn index 1f1f317..91245df 100644 --- a/lib/stormpy/examples/files/ctmc/dft.drn +++ b/lib/stormpy/examples/files/ctmc/dft.drn @@ -3,71 +3,73 @@ @type: CTMC @parameters +@reward_models + @nr_states 16 @model -state 0 init +state 0 !1 failed action 0 - 1 : 0.5 - 2 : 0.5 - 3 : 0.5 - 4 : 0.5 -state 1 - action 0 - 5 : 0.5 - 9 : 0.5 - 11 : 0.5 -state 2 - action 0 - 5 : 0.5 - 14 : 0.5 - 15 : 0.5 -state 3 + 0 : 1 +state 1 !2 init action 0 + 2 : 0.5 9 : 0.5 - 12 : 0.5 + 13 : 0.5 15 : 0.5 -state 4 - action 0 - 11 : 0.5 - 12 : 0.5 - 14 : 0.5 -state 5 +state 2 !1.5 action 0 + 3 : 0.5 6 : 0.5 8 : 0.5 -state 6 +state 3 !1 action 0 - 7 : 0.5 -state 7 failed + 4 : 0.5 + 5 : 0.5 +state 4 !0.5 + action 0 + 0 : 0.5 +state 5 !0.5 action 0 - 7 : 1 -state 8 + 0 : 0.5 +state 6 !1 action 0 + 4 : 0.5 7 : 0.5 -state 9 +state 7 !0.5 action 0 - 8 : 0.5 - 10 : 0.5 -state 10 + 0 : 0.5 +state 8 !1 action 0 + 5 : 0.5 7 : 0.5 -state 11 +state 9 !1.5 action 0 - 6 : 0.5 + 3 : 0.5 10 : 0.5 -state 12 + 12 : 0.5 +state 10 !1 action 0 - 10 : 0.5 - 13 : 0.5 -state 13 + 4 : 0.5 + 11 : 0.5 +state 11 !0.5 action 0 - 7 : 0.5 -state 14 + 0 : 0.5 +state 12 !1 action 0 - 6 : 0.5 - 13 : 0.5 -state 15 + 5 : 0.5 + 11 : 0.5 +state 13 !1.5 action 0 8 : 0.5 - 13 : 0.5 + 12 : 0.5 + 14 : 0.5 +state 14 !1 + action 0 + 7 : 0.5 + 11 : 0.5 +state 15 !1.5 + action 0 + 6 : 0.5 + 10 : 0.5 + 14 : 0.5 diff --git a/tests/core/test_modelchecking.py b/tests/core/test_modelchecking.py index e4df9eb..89cce9e 100644 --- a/tests/core/test_modelchecking.py +++ b/tests/core/test_modelchecking.py @@ -124,6 +124,6 @@ class TestModelChecking: assert model.nr_transitions == 33 assert len(model.initial_states) == 1 initial_state = model.initial_states[0] - assert initial_state == 0 + assert initial_state == 1 result = stormpy.model_checking(model, formulas[0]) assert math.isclose(result.at(initial_state), 4.166666667) From 5ba71f81b5f66651690b6f82dbdcbddca88f9a1c Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 18 Apr 2018 17:52:16 +0200 Subject: [PATCH 017/147] Travis: test against stable version of Storm as well --- .travis.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c79c06..6972611 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ notifications: # jobs: include: - # docker storm:latest + # Docker Storm master - os: linux compiler: gcc env: TASK=Test CONFIG=Release DOCKER=storm:travis PYTHON=python3 @@ -33,7 +33,7 @@ jobs: travis/install_linux.sh script: travis/build.sh - # docker storm-debug:latest + # Docker Storm master in debug mode - os: linux compiler: gcc env: TASK=Test CONFIG=Debug DOCKER=storm:travis-debug PYTHON=python3 @@ -41,6 +41,22 @@ jobs: travis/install_linux.sh script: travis/build.sh + # Docker Storm stable + - os: linux + compiler: gcc + env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 + install: + travis/install_linux.sh + script: + travis/build.sh + # Docker Storm stable in debug mode + - os: linux + compiler: gcc + env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 + install: + travis/install_linux.sh + script: + travis/build.sh # Documentation - os: linux compiler: gcc @@ -59,3 +75,8 @@ jobs: on: branch: master +allow_failures: + - os: linux + env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 + - os: linux + env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 From d6056d71fa877905780a9dc81da2f0c046ba278d Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 18 Apr 2018 20:31:44 +0200 Subject: [PATCH 018/147] Travis: try to fix allow_failures --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6972611..4ddd9ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,8 +75,11 @@ jobs: on: branch: master +# Allow failures of stable versions as new features might have been added allow_failures: - os: linux + compiler: gcc env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 - os: linux + compiler: gcc env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 From 1ac10663252a132dfc191c957ab4fae27af58e26 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 18 Apr 2018 20:39:02 +0200 Subject: [PATCH 019/147] Increased required Storm version --- CHANGELOG.md | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8479b19..41f4233 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Changelog Version 1.2.x ------------- +### Version 1.2.x +Requires storm version >= 1.2.2 and pycarl version >= 2.0.2 + ### Version 1.2.0 Requires storm version >= 1.2.0 and pycarl version >= 2.0.2 - Adaptions to changes in Storm diff --git a/setup.py b/setup.py index 6aba669..a8ecc9a 100755 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ if sys.version_info[0] == 2: sys.exit('Sorry, Python 2.x is not supported') # Minimal storm version required -storm_min_version = "1.2.0" +storm_min_version = "1.2.2" class CMakeExtension(Extension): From 8de8570d11640783e845782d5351d2daa832f574 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 19 Apr 2018 15:03:34 +0200 Subject: [PATCH 020/147] - more expression handling - smt wrap --- src/mod_utility.cpp | 3 ++ src/storage/expressions.cpp | 25 +++++++++++++-- src/utility/smtsolver.cpp | 32 +++++++++++++++++++ src/utility/smtsolver.h | 5 +++ tests/utility/test_smtsolver.py | 55 +++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/utility/smtsolver.cpp create mode 100644 src/utility/smtsolver.h create mode 100644 tests/utility/test_smtsolver.py diff --git a/src/mod_utility.cpp b/src/mod_utility.cpp index 1b6870c..8d1a504 100644 --- a/src/mod_utility.cpp +++ b/src/mod_utility.cpp @@ -1,9 +1,12 @@ #include "common.h" #include "utility/shortestPaths.h" +#include "utility/smtsolver.h" + PYBIND11_MODULE(utility, m) { m.doc() = "Utilities for Storm"; define_ksp(m); + define_smt(m); } diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index 3e564ab..00799ec 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -7,6 +7,9 @@ //Define python bindings void define_expressions(py::module& m) { + using Expression = storm::expressions::Expression; + + // ExpressionManager py::class_>(m, "ExpressionManager", "Manages variables for expressions") @@ -16,7 +19,11 @@ void define_expressions(py::module& m) { .def("create_rational", [](storm::expressions::ExpressionManager const& manager, storm::RationalNumber number) { return manager.rational(number); }, py::arg("rational"), "Create expression from rational number") - ; + .def("create_boolean_variable", &storm::expressions::ExpressionManager::declareBooleanVariable, "create Boolean variable", py::arg("name"), py::arg("auxiliary") = false) + .def("create_integer_variable", &storm::expressions::ExpressionManager::declareIntegerVariable, "create Integer variable", py::arg("name"), py::arg("auxiliary") = false) + .def("create_rational_variable", &storm::expressions::ExpressionManager::declareRationalVariable, "create Rational variable", py::arg("name"), py::arg("auxiliary") = false) + + ; // Variable py::class_>(m, "Variable", "Represents a variable") @@ -39,7 +46,21 @@ void define_expressions(py::module& m) { .def("has_rational_type", &storm::expressions::Expression::hasRationalType, "Check if the expression is a rational") .def_property_readonly("type", &storm::expressions::Expression::getType, "Get the Type") .def("__str__", &storm::expressions::Expression::toString, "To string") - ; + + .def_static("plus", [](Expression const& lhs, Expression const& rhs) {return lhs + rhs;}) + .def_static("minus", [](Expression const& lhs, Expression const& rhs) {return lhs - rhs;}) + .def_static("multiply", [](Expression const& lhs, Expression const& rhs) {return lhs * rhs;}) + .def_static("and", [](Expression const& lhs, Expression const& rhs) {return lhs && rhs;}) + .def_static("or", [](Expression const& lhs, Expression const& rhs) {return lhs || rhs;}) + .def_static("geq", [](Expression const& lhs, Expression const& rhs) {return lhs >= rhs;}) + .def_static("eq", [](Expression const& lhs, Expression const& rhs) {return lhs == rhs;}) + .def_static("neq", [](Expression const& lhs, Expression const& rhs) {return lhs != rhs;}) + .def_static("greater", [](Expression const& lhs, Expression const& rhs) {return lhs > rhs;}) + .def_static("less", [](Expression const& lhs, Expression const& rhs) {return lhs < rhs;}) + .def_static("leq", [](Expression const& lhs, Expression const& rhs) {return lhs <= rhs;}) + .def_static("implies", [](Expression const& lhs, Expression const& rhs) {return storm::expressions::implies(lhs, rhs);}) + .def_static("iff", [](Expression const& lhs, Expression const& rhs) {return storm::expressions::iff(lhs, rhs);}) + ; py::class_(m, "ExpressionParser", "Parser for storm-expressions") .def(py::init(), "Expression Manager to use", py::arg("expression_manager")) diff --git a/src/utility/smtsolver.cpp b/src/utility/smtsolver.cpp new file mode 100644 index 0000000..4e8e258 --- /dev/null +++ b/src/utility/smtsolver.cpp @@ -0,0 +1,32 @@ +#include "smtsolver.h" +#include +#include "storm/storage/expressions/ExpressionManager.h" + +void define_smt(py::module& m) { + using SmtSolver = storm::solver::SmtSolver; + using Z3SmtSolver = storm::solver::Z3SmtSolver; + using ModelReference = storm::solver::SmtSolver::ModelReference; + + py::enum_(m, "SmtCheckResult", "Result type") + .value("Sat", SmtSolver::CheckResult::Sat) + .value("Unsat", SmtSolver::CheckResult::Unsat) + .value("Unknown", SmtSolver::CheckResult::Unknown) + ; + + py::class_> modelref(m, "ModelReference", "Lightweight Wrapper around results"); + modelref.def("get_boolean_value", &ModelReference::getBooleanValue, "get a value for a boolean variable", py::arg("variable")) + .def("get_integer_value", &ModelReference::getIntegerValue, "get a value for an integer variable", py::arg("variable")) + .def("get_rational_value", &ModelReference::getRationalValue, "get a value (as double) for an rational variable", py::arg("variable")); + + + py::class_ smtsolver(m, "SmtSolver", "Generic Storm SmtSolver Wrapper"); + smtsolver.def("push", &SmtSolver::push, "push") + .def("pop", [](SmtSolver& solver, uint64_t n) {solver.pop(n);}, "pop", py::arg("levels")) + .def("reset", &SmtSolver::reset, "reset") + .def("add", [](SmtSolver& solver, storm::expressions::Expression const& expr) {solver.add(expr);}, "addconstraint") + .def("check", &SmtSolver::check, "check") + .def_property_readonly("model", &SmtSolver::getModel, "get the model"); + + py::class_ z3solver(m, "Z3SmtSolver", "z3 API for storm smtsolver wrapper", smtsolver); + z3solver.def(pybind11::init()); +} \ No newline at end of file diff --git a/src/utility/smtsolver.h b/src/utility/smtsolver.h new file mode 100644 index 0000000..d23a479 --- /dev/null +++ b/src/utility/smtsolver.h @@ -0,0 +1,5 @@ +#pragma once + +#include "src/common.h" + +void define_smt(py::module& m); \ No newline at end of file diff --git a/tests/utility/test_smtsolver.py b/tests/utility/test_smtsolver.py new file mode 100644 index 0000000..7655f3a --- /dev/null +++ b/tests/utility/test_smtsolver.py @@ -0,0 +1,55 @@ +import stormpy +import stormpy.utility + +import pytest + +class TestSmtSolver(): + def test_smtsolver_init(self): + manager = stormpy.ExpressionManager() + solver = stormpy.utility.Z3SmtSolver(manager) + + def test_smtsolver_trivial(self): + manager = stormpy.ExpressionManager() + solver = stormpy.utility.Z3SmtSolver(manager) + solver.add(manager.create_boolean(True)) + assert solver.check() != stormpy.utility.SmtCheckResult.Unsat + assert solver.check() == stormpy.utility.SmtCheckResult.Sat + solver.add(manager.create_boolean(False)) + assert solver.check() == stormpy.utility.SmtCheckResult.Unsat + assert solver.check() != stormpy.utility.SmtCheckResult.Sat + + def test_smtsolver_arithmetic_unsat(self): + manager = stormpy.ExpressionManager() + x = manager.create_integer_variable("x") + xe = x.get_expression() + c1 = stormpy.Expression.geq(xe, manager.create_integer(1)) + c2 = stormpy.Expression.less(xe, manager.create_integer(0)) + solver = stormpy.utility.Z3SmtSolver(manager) + solver.add(c1) + solver.add(c2) + assert solver.check() == stormpy.utility.SmtCheckResult.Unsat + + def test_smtsolver_arithmetic_unsat(self): + manager = stormpy.ExpressionManager() + x = manager.create_integer_variable("x") + xe = x.get_expression() + c1 = stormpy.Expression.geq(xe, manager.create_integer(1)) + c2 = stormpy.Expression.less(xe, manager.create_integer(0)) + solver = stormpy.utility.Z3SmtSolver(manager) + solver.add(c1) + solver.add(c2) + assert solver.check() == stormpy.utility.SmtCheckResult.Unsat + + def test_smtsolver_arithmetic_unsat(self): + manager = stormpy.ExpressionManager() + x = manager.create_integer_variable("x") + xe = x.get_expression() + c1 = stormpy.Expression.geq(xe, manager.create_integer(1)) + c2 = stormpy.Expression.less(xe, manager.create_integer(2)) + solver = stormpy.utility.Z3SmtSolver(manager) + solver.add(c1) + solver.add(c2) + assert solver.check() == stormpy.utility.SmtCheckResult.Sat + assert solver.model.get_integer_value(x) == 1 + + From a1d7266567b0a3439c15143d888eee702bdf21d1 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 20 Apr 2018 16:50:50 +0200 Subject: [PATCH 021/147] Travis: change docker repo --- travis/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis/build.sh b/travis/build.sh index 745305b..209a1b3 100755 --- a/travis/build.sh +++ b/travis/build.sh @@ -11,7 +11,7 @@ linux) docker rm -f stormpy &>/dev/null # Run container set -e - docker run -d -it --name stormpy --privileged mvolk/$DOCKER + docker run -d -it --name stormpy --privileged movesrwth/$DOCKER # Copy local content into container docker exec stormpy mkdir opt/stormpy docker cp . stormpy:/opt/stormpy From aa5c8fb6fe400cea04d129fea09e7c26a1937dd3 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 20 Apr 2018 16:51:17 +0200 Subject: [PATCH 022/147] Travis: minor change in travis script --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ddd9ae..637416b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,11 +75,9 @@ jobs: on: branch: master -# Allow failures of stable versions as new features might have been added -allow_failures: + # Allow failures of stable versions as new features might have been added + allow_failures: - os: linux - compiler: gcc env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 - os: linux - compiler: gcc env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 From bb3b2a8f5e6173a93c3c3462accaf0195ab0243a Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 20 Apr 2018 17:55:52 +0200 Subject: [PATCH 023/147] Travis: use absolute path --- travis/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/travis/build.sh b/travis/build.sh index 209a1b3..2bbe2e8 100755 --- a/travis/build.sh +++ b/travis/build.sh @@ -13,7 +13,7 @@ linux) set -e docker run -d -it --name stormpy --privileged movesrwth/$DOCKER # Copy local content into container - docker exec stormpy mkdir opt/stormpy + docker exec stormpy mkdir /opt/stormpy docker cp . stormpy:/opt/stormpy # Install virtualenv docker exec stormpy apt-get update @@ -27,7 +27,7 @@ linux) export PYTHON=$PYTHON; export CONFIG=$CONFIG; export TASK=$TASK; - cd opt/stormpy; + cd /opt/stormpy; travis/build-helper.sh" exit $? ;; From 9b57e37ee4824236c766d64df8550975d2cb317b Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 25 Apr 2018 15:08:56 +0200 Subject: [PATCH 024/147] Updated gitignore --- .gitignore | 15 +++++++++++---- lib/.gitignore | 4 ---- tests/.gitignore | 1 - 3 files changed, 11 insertions(+), 9 deletions(-) delete mode 100644 lib/.gitignore delete mode 100644 tests/.gitignore diff --git a/.gitignore b/.gitignore index 6c315c3..6250ed7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,14 @@ *.so -__pycache__ +*.py[cod] +lib/**/_config.py +.eggs/ +*.egg-info/ build/ -*.pye -.idea +dist/ +.idea/ +__pycache__/ +_build/ +.pytest_cache/ +.idea/ + .DS_Store -_config.py diff --git a/lib/.gitignore b/lib/.gitignore deleted file mode 100644 index a34a658..0000000 --- a/lib/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.so -__pycache__/ -stormpy.egg-info/ -**/_config.py diff --git a/tests/.gitignore b/tests/.gitignore deleted file mode 100644 index bee8a64..0000000 --- a/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -__pycache__ From da81f6cf1ab94ae0ef748550883a69cebb1d405c Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 25 Apr 2018 15:10:13 +0200 Subject: [PATCH 025/147] Raise ImportError when using python 2.x --- lib/stormpy/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index f1ee516..91da0a8 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -1,3 +1,8 @@ +import sys + +if sys.version_info[0] == 2: + raise ImportError('Python 2.x is not supported for stormpy.') + from . import core from .core import * from . import storage From 95da370b4ad85dbecb4dfa87685b627d2f60f3bb Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 25 Apr 2018 16:10:22 +0200 Subject: [PATCH 026/147] Improve distribution --- MANIFEST.in | 5 +++++ setup.py | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e66eb4a --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include CMakeLists.txt +recursive-include setup/ *.py +recursive-include cmake/ * +recursive-include src/ * +recursive-include resources/ * diff --git a/setup.py b/setup.py index a8ecc9a..77d1f5a 100755 --- a/setup.py +++ b/setup.py @@ -17,6 +17,9 @@ if sys.version_info[0] == 2: # Minimal storm version required storm_min_version = "1.2.2" +# Get the long description from the README file +with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md'), encoding='utf-8') as f: + long_description = f.read() class CMakeExtension(Extension): def __init__(self, name, sourcedir='', subdir=''): @@ -210,7 +213,7 @@ setup( maintainer_email="sebastian.junges@cs.rwth-aachen.de", url="http://moves.rwth-aachen.de", description="stormpy - Python Bindings for Storm", - long_description='', + long_description=long_description, packages=['stormpy', 'stormpy.info', 'stormpy.logic', 'stormpy.storage', 'stormpy.utility', 'stormpy.pars', 'stormpy.dft'], package_dir={'': 'lib'}, From 0b6dd8c0d888835fa3a215d1e83037bb6c0120b0 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 25 Apr 2018 17:01:36 +0200 Subject: [PATCH 027/147] Fix sphinx warning in comment --- lib/stormpy/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 91da0a8..8a47e13 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -147,8 +147,7 @@ def model_checking(model, property, only_initial_states=False, extract_scheduler Perform model checking on model for property. :param model: Model. :param property: Property to check for. - :param only_initial_states: If True, only results for initial states are computed. - If False, results for all states are computed. + :param only_initial_states: If True, only results for initial states are computed, otherwise for all states. :param extract_scheduler: If True, try to extract a scheduler :return: Model checking result. :rtype: CheckResult From a83e2206213c21296557f0d4d5bf9c2db74f6419 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 25 Apr 2018 17:30:57 +0200 Subject: [PATCH 028/147] Updated package information --- setup.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 77d1f5a..c4ce901 100755 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ storm_min_version = "1.2.2" with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md'), encoding='utf-8') as f: long_description = f.read() + class CMakeExtension(Extension): def __init__(self, name, sourcedir='', subdir=''): Extension.__init__(self, name, sources=[]) @@ -214,8 +215,24 @@ setup( url="http://moves.rwth-aachen.de", description="stormpy - Python Bindings for Storm", long_description=long_description, - packages=['stormpy', 'stormpy.info', 'stormpy.logic', 'stormpy.storage', 'stormpy.utility', - 'stormpy.pars', 'stormpy.dft'], + project_urls={ + 'Documentation': 'https://moves-rwth.github.io/stormpy/', + 'Source': 'https://github.com/moves-rwth/stormpy/', + 'Tracker': 'https://github.com/moves-rwth/stormpy/issues', + }, + classifiers=[ + 'Intended Audience :: Science/Research', + 'Topic :: Scientific/Engineering', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + + packages=['stormpy', + 'stormpy.info', + 'stormpy.logic', + 'stormpy.storage', + 'stormpy.utility', + 'stormpy.dft', + 'stormpy.pars'], package_dir={'': 'lib'}, ext_package='stormpy', ext_modules=[CMakeExtension('core', subdir=''), @@ -224,8 +241,8 @@ setup( CMakeExtension('storage', subdir='storage'), CMakeExtension('utility', subdir='utility'), CMakeExtension('dft', subdir='dft'), - CMakeExtension('pars', subdir='pars'), - ], + CMakeExtension('pars', subdir='pars')], + cmdclass={'build_ext': CMakeBuild, 'test': PyTest}, zip_safe=False, install_requires=['pycarl>=2.0.2'], From d0534cd0669b0a95f4a28e7544501aed20c03bcf Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 25 Apr 2018 17:44:07 +0200 Subject: [PATCH 029/147] Minor update in project description --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c4ce901..ae82db1 100755 --- a/setup.py +++ b/setup.py @@ -215,10 +215,11 @@ setup( url="http://moves.rwth-aachen.de", description="stormpy - Python Bindings for Storm", long_description=long_description, + long_description_content_type='text/text/markdown', project_urls={ 'Documentation': 'https://moves-rwth.github.io/stormpy/', 'Source': 'https://github.com/moves-rwth/stormpy/', - 'Tracker': 'https://github.com/moves-rwth/stormpy/issues', + 'Bug reports': 'https://github.com/moves-rwth/stormpy/issues', }, classifiers=[ 'Intended Audience :: Science/Research', From bef2feceb29c8e0dd103a536af2e424ef784f532 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 26 Apr 2018 19:17:18 +0200 Subject: [PATCH 030/147] Removed fixed lib path in cmake --- CMakeLists.txt | 35 ++++++++++------------------------- cmake/CMakeLists.txt | 12 +++++------- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c400ebd..f0d0130 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,57 +1,42 @@ cmake_minimum_required(VERSION 3.0.0) project(pystorm) -option(STORMPY_DISABLE_SIGNATURE_DOC "disables the signature in the documentation" ON) - find_package(storm REQUIRED) add_subdirectory(resources/pybind11) +option(STORMPY_DISABLE_SIGNATURE_DOC "disables the signature in the documentation" ON) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/generated/config.h) message(STATUS "STORM-DIR: ${storm_DIR}") -set(STORMPY_LIB_DIR "${CMAKE_SOURCE_DIR}/lib/stormpy" CACHE STRING "Sets the storm library dir") - function(stormpy_module NAME) - # second, optional argument is LIBRARY_OUTPUT_DIRECTORY, - # defaults to subdir with module name - # third, optional argument are ADDITIONAL_LIBRARIES - # fourth, optional argument are ADDITIONAL_INCLUDES - if(ARGC GREATER 1) - set(LIB_PATH "${ARGV1}") - else() - set(LIB_PATH "${STORMPY_LIB_DIR}/${NAME}") - endif() + # second, optional argument are ADDITIONAL_LIBRARIES + # third, optional argument are ADDITIONAL_INCLUDES file(GLOB_RECURSE "STORM_${NAME}_SOURCES" "${CMAKE_CURRENT_SOURCE_DIR}/src/${NAME}/*.cpp") pybind11_add_module(${NAME} "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_${NAME}.cpp" ${STORM_${NAME}_SOURCES}) - if(ARGC GREATER 2) + if(ARGC GREATER 1) # Additional libraries - target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${ARGV3} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) - target_link_libraries(${NAME} PRIVATE storm ${ARGV2}) + target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${ARGV2} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) + target_link_libraries(${NAME} PRIVATE storm ${ARGV1}) else() target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) target_link_libraries(${NAME} PRIVATE storm) endif() - - # setup.py will override this (because pip may want a different install - # path), but also specifying it here has the advantage that invoking cmake - # manually uses the correct path if the default is used (i.e. the - # STORMPY_LIB_DIR hardcoded above) - set_target_properties(${NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${LIB_PATH}") endfunction(stormpy_module) -stormpy_module(core "${STORMPY_LIB_DIR}") +stormpy_module(core) stormpy_module(info) stormpy_module(logic) stormpy_module(storage) stormpy_module(utility) if(HAVE_STORM_PARS) - stormpy_module(pars "${STORMPY_LIB_DIR}/pars" storm-pars "${storm-pars_INCLUDE_DIR}") + stormpy_module(pars storm-pars "${storm-pars_INCLUDE_DIR}") endif() if(HAVE_STORM_DFT) - stormpy_module(dft "${STORMPY_LIB_DIR}/dft" storm-dft "${storm-dft_INCLUDE_DIR}") + stormpy_module(dft storm-dft "${storm-dft_INCLUDE_DIR}") endif() diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index ef03051..bbaf9c4 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.0.0) project(storm-version) find_package(storm REQUIRED) + +# Set configuration +set(STORM_DIR ${storm_DIR}) +set(STORM_VERSION ${storm_VERSION}) + # Check for storm-pars if(EXISTS "${storm_DIR}/lib/libstorm-pars.dylib") set(HAVE_STORM_PARS TRUE) @@ -20,10 +25,6 @@ else() set(HAVE_STORM_DFT FALSE) endif() -# Set configuration -set(STORM_DIR ${storm_DIR}) -set(STORM_VERSION ${storm_VERSION}) - if(HAVE_STORM_PARS) set(HAVE_STORM_PARS_BOOL "True") else() @@ -48,7 +49,4 @@ else() set(STORM_CLN_RF_BOOL "False") endif() - - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.py.in ${CMAKE_CURRENT_BINARY_DIR}/generated/config.py @ONLY) From 19db78e038c30a6527be466dd27736267b44d9ef Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 26 Apr 2018 22:18:22 +0200 Subject: [PATCH 031/147] Fixed typo --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ae82db1..1519370 100755 --- a/setup.py +++ b/setup.py @@ -215,7 +215,7 @@ setup( url="http://moves.rwth-aachen.de", description="stormpy - Python Bindings for Storm", long_description=long_description, - long_description_content_type='text/text/markdown', + long_description_content_type='text/markdown', project_urls={ 'Documentation': 'https://moves-rwth.github.io/stormpy/', 'Source': 'https://github.com/moves-rwth/stormpy/', From 0c62edfb0db7f51b617f9cd08ae250bcce5f26d4 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 26 Apr 2018 22:18:51 +0200 Subject: [PATCH 032/147] Added missing package in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 1519370..6a1a851 100755 --- a/setup.py +++ b/setup.py @@ -228,6 +228,7 @@ setup( ], packages=['stormpy', + 'stormpy.exceptions', 'stormpy.info', 'stormpy.logic', 'stormpy.storage', From 5db3c691526f919a863de18dd752fc830a12a06f Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 27 Apr 2018 11:44:10 +0200 Subject: [PATCH 033/147] Updated setup.py --- MANIFEST.in | 1 + setup.py | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index e66eb4a..bbfd747 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,3 +3,4 @@ recursive-include setup/ *.py recursive-include cmake/ * recursive-include src/ * recursive-include resources/ * +recursive-include lib/stormpy/examples/files/ * diff --git a/setup.py b/setup.py index 6a1a851..ec81e39 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ import sys import subprocess import datetime -from setuptools import setup, Extension +from setuptools import setup, Extension, find_packages from setuptools.command.build_ext import build_ext from setuptools.command.test import test from distutils.version import StrictVersion @@ -212,7 +212,7 @@ setup( author_email="matthias.volk@cs.rwth-aachen.de", maintainer="S. Junges", maintainer_email="sebastian.junges@cs.rwth-aachen.de", - url="http://moves.rwth-aachen.de", + url="https://github.com/moves-rwth/stormpy/", description="stormpy - Python Bindings for Storm", long_description=long_description, long_description_content_type='text/markdown', @@ -227,15 +227,10 @@ setup( 'Topic :: Software Development :: Libraries :: Python Modules', ], - packages=['stormpy', - 'stormpy.exceptions', - 'stormpy.info', - 'stormpy.logic', - 'stormpy.storage', - 'stormpy.utility', - 'stormpy.dft', - 'stormpy.pars'], + packages=find_packages('lib'), package_dir={'': 'lib'}, + include_package_data=True, + package_data={'stormpy.examples': ['examples/files/*']}, ext_package='stormpy', ext_modules=[CMakeExtension('core', subdir=''), CMakeExtension('info', subdir='info'), From 4c40ab6ea306d8b54c74c8255a085678c76d051a Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 27 Apr 2018 14:27:18 +0200 Subject: [PATCH 034/147] Use pytest-runner --- setup.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/setup.py b/setup.py index ec81e39..ef1227c 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,6 @@ import datetime from setuptools import setup, Extension, find_packages from setuptools.command.build_ext import build_ext -from setuptools.command.test import test from distutils.version import StrictVersion import setup.helper as setup_helper @@ -197,14 +196,6 @@ class CMakeBuild(build_ext): subprocess.check_call(['cmake', '--build', '.', '--target', ext.name] + build_args, cwd=self.build_temp) -class PyTest(test): - def run_tests(self): - # import here, cause outside the eggs aren't loaded - import pytest - errno = pytest.main(['tests']) - sys.exit(errno) - - setup( name="stormpy", version=setup_helper.obtain_version(), @@ -240,7 +231,7 @@ setup( CMakeExtension('dft', subdir='dft'), CMakeExtension('pars', subdir='pars')], - cmdclass={'build_ext': CMakeBuild, 'test': PyTest}, + cmdclass={'build_ext': CMakeBuild}, zip_safe=False, install_requires=['pycarl>=2.0.2'], setup_requires=['pytest-runner'], From 89ed130335346364006f0e41936c4d563e1ec5f7 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Mon, 30 Apr 2018 17:43:07 +0200 Subject: [PATCH 035/147] Extended documentation --- doc/source/advanced_topics.rst | 6 +- doc/source/api.rst | 17 +++++ .../{code_stormpy_core.rst => api/core.rst} | 8 --- doc/source/api/dft.rst | 7 ++ doc/source/api/exceptions.rst | 7 ++ doc/source/api/info.rst | 7 ++ doc/source/api/logic.rst | 7 ++ doc/source/api/pars.rst | 7 ++ doc/source/api/storage.rst | 7 ++ doc/source/api/utility.rst | 7 ++ doc/source/code_stormpy_logic.rst | 12 ---- doc/source/code_stormpy_storage.rst | 11 ---- doc/source/conf.py | 2 +- doc/source/contributors.rst | 3 +- doc/source/{ => doc}/building_models.rst | 0 doc/source/{ => doc}/reward_models.rst | 0 doc/source/{ => doc}/shortest_paths.rst | 0 doc/source/getting_started.rst | 10 +-- doc/source/index.rst | 12 +--- doc/source/installation.rst | 66 ++++++++++++++----- 20 files changed, 129 insertions(+), 67 deletions(-) create mode 100644 doc/source/api.rst rename doc/source/{code_stormpy_core.rst => api/core.rst} (53%) create mode 100644 doc/source/api/dft.rst create mode 100644 doc/source/api/exceptions.rst create mode 100644 doc/source/api/info.rst create mode 100644 doc/source/api/logic.rst create mode 100644 doc/source/api/pars.rst create mode 100644 doc/source/api/storage.rst create mode 100644 doc/source/api/utility.rst delete mode 100644 doc/source/code_stormpy_logic.rst delete mode 100644 doc/source/code_stormpy_storage.rst rename doc/source/{ => doc}/building_models.rst (100%) rename doc/source/{ => doc}/reward_models.rst (100%) rename doc/source/{ => doc}/shortest_paths.rst (100%) diff --git a/doc/source/advanced_topics.rst b/doc/source/advanced_topics.rst index 124c702..7c5d0c0 100644 --- a/doc/source/advanced_topics.rst +++ b/doc/source/advanced_topics.rst @@ -8,6 +8,6 @@ This guide is a collection of examples meant to bridge the gap between the getti :maxdepth: 2 :caption: Contents: - building_models - reward_models - shortest_paths \ No newline at end of file + doc/building_models + doc/reward_models + doc/shortest_paths \ No newline at end of file diff --git a/doc/source/api.rst b/doc/source/api.rst new file mode 100644 index 0000000..f9144c4 --- /dev/null +++ b/doc/source/api.rst @@ -0,0 +1,17 @@ +Stormpy API Reference +==================================== +Work in progress! + +.. toctree:: + :maxdepth: 2 + :caption: Modules: + + api/core + api/info + api/exceptions + api/logic + api/storage + api/utility + + api/dft + api/pars diff --git a/doc/source/code_stormpy_core.rst b/doc/source/api/core.rst similarity index 53% rename from doc/source/code_stormpy_core.rst rename to doc/source/api/core.rst index 4f26fa9..b9a41a1 100644 --- a/doc/source/code_stormpy_core.rst +++ b/doc/source/api/core.rst @@ -5,11 +5,3 @@ Stormpy.core :members: :undoc-members: :imported-members: - -Core members -========================= - - -.. automodule:: stormpy.core - :members: - :undoc-members: diff --git a/doc/source/api/dft.rst b/doc/source/api/dft.rst new file mode 100644 index 0000000..331f60c --- /dev/null +++ b/doc/source/api/dft.rst @@ -0,0 +1,7 @@ +Stormpy.dft +************************** + +.. automodule:: stormpy.dft + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/api/exceptions.rst b/doc/source/api/exceptions.rst new file mode 100644 index 0000000..8a3e2b2 --- /dev/null +++ b/doc/source/api/exceptions.rst @@ -0,0 +1,7 @@ +Stormpy.exceptions +************************** + +.. automodule:: stormpy.exceptions + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/api/info.rst b/doc/source/api/info.rst new file mode 100644 index 0000000..8421b2b --- /dev/null +++ b/doc/source/api/info.rst @@ -0,0 +1,7 @@ +Stormpy.info +************************** + +.. automodule:: stormpy.info + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/api/logic.rst b/doc/source/api/logic.rst new file mode 100644 index 0000000..84d34a9 --- /dev/null +++ b/doc/source/api/logic.rst @@ -0,0 +1,7 @@ +Stormpy.logic +************************** + +.. automodule:: stormpy.logic + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/api/pars.rst b/doc/source/api/pars.rst new file mode 100644 index 0000000..2c66395 --- /dev/null +++ b/doc/source/api/pars.rst @@ -0,0 +1,7 @@ +Stormpy.pars +************************** + +.. automodule:: stormpy.pars + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/api/storage.rst b/doc/source/api/storage.rst new file mode 100644 index 0000000..5281904 --- /dev/null +++ b/doc/source/api/storage.rst @@ -0,0 +1,7 @@ +Stormpy.storage +************************** + +.. automodule:: stormpy.storage + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/api/utility.rst b/doc/source/api/utility.rst new file mode 100644 index 0000000..6bfe305 --- /dev/null +++ b/doc/source/api/utility.rst @@ -0,0 +1,7 @@ +Stormpy.utility +************************** + +.. automodule:: stormpy.utility + :members: + :undoc-members: + :imported-members: diff --git a/doc/source/code_stormpy_logic.rst b/doc/source/code_stormpy_logic.rst deleted file mode 100644 index 51c4daf..0000000 --- a/doc/source/code_stormpy_logic.rst +++ /dev/null @@ -1,12 +0,0 @@ -Stormpy.logic -************************** - -.. automodule:: stormpy - - -Members -========================= - - -.. automodule:: stormpy.logic.logic - :members: diff --git a/doc/source/code_stormpy_storage.rst b/doc/source/code_stormpy_storage.rst deleted file mode 100644 index 666f940..0000000 --- a/doc/source/code_stormpy_storage.rst +++ /dev/null @@ -1,11 +0,0 @@ -Stormpy.storage -************************** - -.. automodule:: stormpy - -Members -============================== - -.. automodule:: stormpy.storage.storage - :members: - diff --git a/doc/source/conf.py b/doc/source/conf.py index 2fbcfa8..c1a41d1 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -56,7 +56,7 @@ master_doc = 'index' # General information about the project. project = 'stormpy' -copyright = '2016--2017 Moves RWTH Aachen' +copyright = '2016--2018 Moves RWTH Aachen' author = 'Sebastian Junges, Matthias Volk' # The version info for the project you're documenting, acts as replacement for diff --git a/doc/source/contributors.rst b/doc/source/contributors.rst index 464d72b..0961349 100644 --- a/doc/source/contributors.rst +++ b/doc/source/contributors.rst @@ -2,7 +2,8 @@ Contributors ************** -Stormpy is an extension to `Storm `_. As a consequence, developers of Storm contributed significantly to the functionality offered by these Python bindings. +Stormpy is an extension to `Storm `_. +As a consequence, developers of Storm contributed significantly to the functionality offered by these Python bindings. The bindings themselves have been developed by (lexicographically ordered): * Sebastian Junges diff --git a/doc/source/building_models.rst b/doc/source/doc/building_models.rst similarity index 100% rename from doc/source/building_models.rst rename to doc/source/doc/building_models.rst diff --git a/doc/source/reward_models.rst b/doc/source/doc/reward_models.rst similarity index 100% rename from doc/source/reward_models.rst rename to doc/source/doc/reward_models.rst diff --git a/doc/source/shortest_paths.rst b/doc/source/doc/shortest_paths.rst similarity index 100% rename from doc/source/shortest_paths.rst rename to doc/source/doc/shortest_paths.rst diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index b577e11..1649cc6 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -11,15 +11,17 @@ This guide is intended for people which have a basic understanding of probabilis `Storm website `_. While we assume some very basic programming concepts, we refrain from using more advanced concepts of python throughout the guide. -We start with a selection of high-level constructs in stormpy, and go into more details afterwards. More in-depth examples can be found in the :doc:`advanced_examples`. +We start with a selection of high-level constructs in stormpy, and go into more details afterwards. More in-depth examples can be found in the :doc:`advanced_topics`. .. seealso:: The code examples are also given in the `examples/ `_ folder. These boxes throughout the text will tell you which example contains the code discussed. -In order to do this, we import stormpy:: +We start by launching the python 3 interpreter:: + + $ python3 + +First we import stormpy:: >>> import stormpy - >>> import stormpy.core - Building models ------------------------------------------------ diff --git a/doc/source/index.rst b/doc/source/index.rst index 76040e8..74a137d 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -19,16 +19,8 @@ Stormpy is a set of python bindings for the probabilistic model checker `Storm < advanced_topics contributors - -Stormpy API Reference -==================================== -.. toctree:: - :maxdepth: 2 - :caption: Modules: - - code_stormpy_core - code_stormpy_logic - code_stormpy_storage + +.. include:: api.rst Indices and tables diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 69d2915..858da51 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -7,28 +7,46 @@ Requirements Before installing stormpy, make sure -- `pycarl `_ -- `Storm `_ +- Python 3 is available on your system. Stormpy does not work with python 2. +- `pycarl `_ is available. +- `Storm `_ is available on your system. -are both available on your system. To avoid issues, we suggest that both use the same version of `carl `_. +To avoid issues, we suggest that both pycarl and Storm use the same version of `carl `_. The simplest way of ensuring this is to first install carl as explained in the `Storm installation guide `_. You can then install Storm and pycarl independently. -.. topic:: Virtual Environments - - Virtual environments create isolated environments for your projects. This helps to keep your system clean, work with different versions of packages and different versions of python. While it is not required, we recommend the use of - such virtual environments. To get you started, we recommend `this guide `_ or `this primer `_. - Installation Steps ==================== +Virtual Environments +-------------------- + +Virtual environments create isolated environments for your projects. +This helps to keep your system clean, work with different versions of packages and different version of python. +While it is not required, we recommend the use of such virtual environments. To get you started, we recommend +`this guide `_ or +`this primer `_. + +In short you can create a virtual environment ``env`` with:: + + $ pip install virtualenv + $ virtualenv -p python3 env + $ source env/bin/activate + +The last step activates the virtual environment. +Whenever using the environment the console prompt is prefixed with ``(env)``. + + +Building stormpy +---------------- + Clone stormpy into any suitable location:: $ git clone https://github.com/moves-rwth/stormpy.git $ cd stormpy -Here, build stormpy in develop mode using your favourite python distribution way of installing: e.g.:: +Build stormpy in develop mode using your favourite python distribution way of installing: e.g.:: $ python3 setup.py develop @@ -37,17 +55,31 @@ or:: $ pip install -ve . -.. topic:: Specifying which Storm library to use +Optional build arguments +^^^^^^^^^^^^^^^^^^^^^^^^ + +The build step also takes optional arguments for a more advanced configuration of stormpy. + +* *Specifying which Storm library to use* + + If you have multiple versions of Storm or cmake is not able to find your Storm version, + you can specify the ``--storm-dir YOUR-PATH-TO-STORM`` flag in the ``build_ext`` step:: - If you have multiple versions of Storm or cmake is not able to find your Storm version, - you can specify the `--storm-dir YOUR-PATH-TO-STORM` flag in the build_ext step:: - $ python3 setup.py build_ext --storm-dir YOUR-PATH-TO-STORM develop - + +* *Building stormpy in debug mode* + + If you want to build stormpy in debug mode you can add the ``--debug`` flag in the ``build_ext`` step:: + + $ python3 setup.py build_ext --debug develop + + +Testing stormpy installation +---------------------------- + After building, you can run the test files by:: py.test tests/ -If tests pass, you can continue with our :doc:`getting_started`. - - +If the tests pass, you can now use stormpy. +To get started, continue with our :doc:`getting_started`, consult the test files in ``tests/`` or the :doc:`api` (work in progress). From 4daa73372750292c1dca3bb944786822a8608870 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 1 May 2018 20:28:13 +0200 Subject: [PATCH 036/147] Moved documentation for parametric models into own file --- doc/source/advanced_topics.rst | 3 +- doc/source/doc/parametric_models.rst | 61 +++++++++++++++++++ doc/source/getting_started.rst | 49 +-------------- examples/04-getting-started.py | 37 ++++------- examples/06-getting-started.py | 26 -------- .../parametric_models/01-parametric-models.py | 41 +++++++++++++ .../02-parametric-models.py} | 4 +- 7 files changed, 119 insertions(+), 102 deletions(-) create mode 100644 doc/source/doc/parametric_models.rst delete mode 100644 examples/06-getting-started.py create mode 100644 examples/parametric_models/01-parametric-models.py rename examples/{05-getting-started.py => parametric_models/02-parametric-models.py} (95%) diff --git a/doc/source/advanced_topics.rst b/doc/source/advanced_topics.rst index 7c5d0c0..765149a 100644 --- a/doc/source/advanced_topics.rst +++ b/doc/source/advanced_topics.rst @@ -10,4 +10,5 @@ This guide is a collection of examples meant to bridge the gap between the getti doc/building_models doc/reward_models - doc/shortest_paths \ No newline at end of file + doc/shortest_paths + doc/parametric_models \ No newline at end of file diff --git a/doc/source/doc/parametric_models.rst b/doc/source/doc/parametric_models.rst new file mode 100644 index 0000000..c4376c4 --- /dev/null +++ b/doc/source/doc/parametric_models.rst @@ -0,0 +1,61 @@ +***************** +Parametric Models +***************** + + + +Instantiating parametric models +=============================== +.. seealso:: `01-parametric-models.py `_ + +Input formats such as prism allow to specify programs with open constants. We refer to these open constants as parameters. +If the constants only influence the probabilities or rates, but not the topology of the underlying model, we can build these models as parametric models:: + + >>> import stormpy + >>> import stormpy.examples + >>> import stormpy.examples.files + >>> path = stormpy.examples.files.prism_dtmc_die + >>> prism_program = stormpy.parse_prism_program(path) + >>> formula_str = "P=? [F s=7 & d=2]" + >>> properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program) + + >>> model = stormpy.build_parametric_model(prism_program, properties) + >>> parameters = model.collect_probability_parameters() + >>> for x in parameters: + ... print(x) + +In order to obtain a standard DTMC, MDP or other Markov model, we need to instantiate these models by means of a model instantiator:: + + >>> import stormpy.pars + >>> instantiator = stormpy.pars.PDtmcInstantiator(model) + +Before we obtain an instantiated model, we need to map parameters to values: We build such a dictionary as follows:: + + >>> point = dict() + >>> for x in parameters: + ... print(x.name) + ... point[x] = 0.4 + >>> instantiated_model = instantiator.instantiate(point) + >>> result = stormpy.model_checking(instantiated_model, properties[0]) + +Initial states and labels are set as for the parameter-free case. + + +Checking parametric models +========================== +.. seealso:: `02-parametric-models.py `_ + +It is also possible to check the parametric model directly, similar as before in :ref:`getting-started-checking-properties`:: + + >>> result = stormpy.model_checking(model, properties[0]) + >>> initial_state = model.initial_states[0] + >>> func = result.at(initial_state) + +We collect the constraints ensuring that underlying model is well-formed and the graph structure does not change. + + >>> collector = stormpy.ConstraintCollector(model) + >>> for formula in collector.wellformed_constraints: + ... print(formula) + >>> for formula in collector.graph_preserving_constraints: + ... print(formula) + diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index 1649cc6..947036a 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -126,58 +126,11 @@ A good way to get the result for the initial states is as follows:: >>> print(result.at(initial_state)) 0.5 -Instantiating parametric models ------------------------------------- -.. seealso:: `04-getting-started.py `_ - -Input formats such as prism allow to specify programs with open constants. We refer to these open constants as parameters. -If the constants only influence the probabilities or rates, but not the topology of the underlying model, we can build these models as parametric models:: - - >>> model = stormpy.build_parametric_model(prism_program, properties) - >>> parameters = model.collect_probability_parameters() - >>> for x in parameters: - ... print(x) - -In order to obtain a standard DTMC, MDP or other Markov model, we need to instantiate these models by means of a model instantiator:: - - >>> import stormpy.pars - >>> instantiator = stormpy.pars.PDtmcInstantiator(model) - -Before we obtain an instantiated model, we need to map parameters to values: We build such a dictionary as follows:: - - >>> point = dict() - >>> for x in parameters: - ... print(x.name) - ... point[x] = 0.4 - >>> instantiated_model = instantiator.instantiate(point) - >>> result = stormpy.model_checking(instantiated_model, properties[0]) - -Initial states and labels are set as for the parameter-free case. - - -Checking parametric models ------------------------------------- -.. seealso:: `05-getting-started.py `_ - -It is also possible to check the parametric model directly, similar as before in :ref:`getting-started-checking-properties`:: - - >>> result = stormpy.model_checking(model, properties[0]) - >>> initial_state = model.initial_states[0] - >>> func = result.at(initial_state) - -We collect the constraints ensuring that underlying model is well-formed and the graph structure does not change. - - >>> collector = stormpy.ConstraintCollector(model) - >>> for formula in collector.wellformed_constraints: - ... print(formula) - >>> for formula in collector.graph_preserving_constraints: - ... print(formula) - .. _getting-started-investigating-the-model: Investigating the model ------------------------------------- -.. seealso:: `06-getting-started.py `_ +.. seealso:: `04-getting-started.py `_ One powerful part of the Storm model checker is to quickly create the Markov chain from higher-order descriptions, as seen above:: diff --git a/examples/04-getting-started.py b/examples/04-getting-started.py index a5c0d60..e1fcd66 100644 --- a/examples/04-getting-started.py +++ b/examples/04-getting-started.py @@ -1,40 +1,27 @@ import stormpy import stormpy.core -import pycarl -import pycarl.core - import stormpy.examples import stormpy.examples.files -import stormpy._config as config - def example_getting_started_04(): - # Check support for parameters - if not config.storm_with_pars: - print("Support parameters is missing. Try building storm-pars.") - return - - import stormpy.pars - path = stormpy.examples.files.prism_pdtmc_die + path = stormpy.examples.files.prism_dtmc_die prism_program = stormpy.parse_prism_program(path) formula_str = "P=? [F s=7 & d=2]" properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program) - model = stormpy.build_parametric_model(prism_program, properties) - print("Model supports parameters: {}".format(model.supports_parameters)) - parameters = model.collect_probability_parameters() - assert len(parameters) == 2 - - instantiator = stormpy.pars.PDtmcInstantiator(model) - point = dict() - for x in parameters: - print(x.name) - point[x] = stormpy.RationalRF(0.4) - instantiated_model = instantiator.instantiate(point) - result = stormpy.model_checking(instantiated_model, properties[0]) - print(result) + model = stormpy.build_model(prism_program, properties) + + print(model.model_type) + + for state in model.states: + if state.id in model.initial_states: + print(state) + for action in state.actions: + for transition in action.transitions: + print("From state {}, with probability {}, go to state {}".format(state, transition.value(), + transition.column)) if __name__ == '__main__': diff --git a/examples/06-getting-started.py b/examples/06-getting-started.py deleted file mode 100644 index e8d1378..0000000 --- a/examples/06-getting-started.py +++ /dev/null @@ -1,26 +0,0 @@ -import stormpy -import stormpy.core - -import stormpy.examples -import stormpy.examples.files - -def example_getting_started_06(): - path = stormpy.examples.files.prism_dtmc_die - prism_program = stormpy.parse_prism_program(path) - - formula_str = "P=? [F s=7 & d=2]" - properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program) - model = stormpy.build_model(prism_program, properties) - - print(model.model_type) - - for state in model.states: - if state.id in model.initial_states: - print(state) - for action in state.actions: - for transition in action.transitions: - print("From state {}, with probability {}, go to state {}".format(state, transition.value(), transition.column)) - - -if __name__ == '__main__': - example_getting_started_06() \ No newline at end of file diff --git a/examples/parametric_models/01-parametric-models.py b/examples/parametric_models/01-parametric-models.py new file mode 100644 index 0000000..30c2d4e --- /dev/null +++ b/examples/parametric_models/01-parametric-models.py @@ -0,0 +1,41 @@ +import stormpy +import stormpy.core + +import pycarl +import pycarl.core + +import stormpy.examples +import stormpy.examples.files + +import stormpy._config as config + + +def example_parametric_models_01(): + # Check support for parameters + if not config.storm_with_pars: + print("Support parameters is missing. Try building storm-pars.") + return + + import stormpy.pars + path = stormpy.examples.files.prism_pdtmc_die + prism_program = stormpy.parse_prism_program(path) + + formula_str = "P=? [F s=7 & d=2]" + properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program) + model = stormpy.build_parametric_model(prism_program, properties) + print("Model supports parameters: {}".format(model.supports_parameters)) + parameters = model.collect_probability_parameters() + assert len(parameters) == 2 + + instantiator = stormpy.pars.PDtmcInstantiator(model) + point = dict() + for x in parameters: + print(x.name) + point[x] = stormpy.RationalRF(0.4) + instantiated_model = instantiator.instantiate(point) + result = stormpy.model_checking(instantiated_model, properties[0]) + print(result) + + +if __name__ == '__main__': + example_parametric_models_01() diff --git a/examples/05-getting-started.py b/examples/parametric_models/02-parametric-models.py similarity index 95% rename from examples/05-getting-started.py rename to examples/parametric_models/02-parametric-models.py index 29403f6..d273c0b 100644 --- a/examples/05-getting-started.py +++ b/examples/parametric_models/02-parametric-models.py @@ -11,7 +11,7 @@ import stormpy.examples.files import stormpy._config as config -def example_getting_started_05(): +def example_parametric_models_02(): # Check support for parameters if not config.storm_with_pars: print("Support parameters is missing. Try building storm-pars.") @@ -45,4 +45,4 @@ def example_getting_started_05(): if __name__ == '__main__': - example_getting_started_05() + example_parametric_models_02() From aad97475725414ca8349b65b8c4432d7fc750e98 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 3 May 2018 13:15:44 +0200 Subject: [PATCH 037/147] Fix for storm-dir --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ef1227c..c6542a8 100755 --- a/setup.py +++ b/setup.py @@ -58,7 +58,7 @@ class CMakeBuild(build_ext): self.config.write_config("build/build_config.cfg") cmake_args = [] - storm_dir = self.config.get_as_string("storm_dir") + storm_dir = os.path.expanduser(self.config.get_as_string("storm_dir")) if storm_dir: cmake_args += ['-Dstorm_DIR=' + storm_dir] _ = subprocess.check_output(['cmake', os.path.abspath("cmake")] + cmake_args, cwd=build_temp_version) From 61a49dc20b9e660241808c54949ccfbf21bd8e32 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 3 May 2018 13:28:24 +0200 Subject: [PATCH 038/147] Extended doc for build arguments --- doc/source/installation.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 858da51..80ce6c7 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -58,21 +58,35 @@ or:: Optional build arguments ^^^^^^^^^^^^^^^^^^^^^^^^ -The build step also takes optional arguments for a more advanced configuration of stormpy. +The build step ``build_ext`` also takes optional arguments for a more advanced configuration of stormpy. * *Specifying which Storm library to use* If you have multiple versions of Storm or cmake is not able to find your Storm version, - you can specify the ``--storm-dir YOUR-PATH-TO-STORM`` flag in the ``build_ext`` step:: + you can specify the ``--storm-dir YOUR-PATH-TO-STORM`` flag:: $ python3 setup.py build_ext --storm-dir YOUR-PATH-TO-STORM develop +* *Disabling functionality* + + If you want to disable certain functionality in stormpy from being built you can use the following flags: + + * ``--disable-dft`` to disable support for dynamic fault trees (DFTs) + * ``--disable-pars`` to disable support for parametric models + * *Building stormpy in debug mode* - If you want to build stormpy in debug mode you can add the ``--debug`` flag in the ``build_ext`` step:: + If you want to build stormpy in debug mode you can add the ``--debug`` flag:: $ python3 setup.py build_ext --debug develop +* *Setting number of build threads* + + The build of stormpy uses all available cores per default. + If you want to configure the number of threads manually you can specify the ``--jobs`` (or ``-j``) flag:: + + $ python3 setup.py build_ext --jobs 2 develop + Testing stormpy installation ---------------------------- From b0132b4317d2f1275d263e371a61e6ef78ad5d97 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 16 May 2018 15:21:09 +0200 Subject: [PATCH 039/147] Travis: removed docker installation as the package is already present --- .travis.yml | 10 ------- travis/install_linux.sh | 5 ---- travis/install_osx.sh | 63 ----------------------------------------- 3 files changed, 78 deletions(-) delete mode 100755 travis/install_linux.sh delete mode 100755 travis/install_osx.sh diff --git a/.travis.yml b/.travis.yml index 637416b..c5c081d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,40 +29,30 @@ jobs: - os: linux compiler: gcc env: TASK=Test CONFIG=Release DOCKER=storm:travis PYTHON=python3 - install: - travis/install_linux.sh script: travis/build.sh # Docker Storm master in debug mode - os: linux compiler: gcc env: TASK=Test CONFIG=Debug DOCKER=storm:travis-debug PYTHON=python3 - install: - travis/install_linux.sh script: travis/build.sh # Docker Storm stable - os: linux compiler: gcc env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 - install: - travis/install_linux.sh script: travis/build.sh # Docker Storm stable in debug mode - os: linux compiler: gcc env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 - install: - travis/install_linux.sh script: travis/build.sh # Documentation - os: linux compiler: gcc env: TASK=Documentation CONFIG=Release DOCKER=storm:travis PYTHON=python3 - install: - travis/install_linux.sh script: travis/build.sh before_deploy: diff --git a/travis/install_linux.sh b/travis/install_linux.sh deleted file mode 100755 index 911b673..0000000 --- a/travis/install_linux.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -set -e - -sudo apt-get install -qq -y docker diff --git a/travis/install_osx.sh b/travis/install_osx.sh deleted file mode 100755 index b7cec33..0000000 --- a/travis/install_osx.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash -# Script installing dependencies -# Inspired by https://github.com/google/fruit - -set -e - -# Helper for travis folding -travis_fold() { - local action=$1 - local name=$2 - echo -en "travis_fold:${action}:${name}\r" -} - -# Helper for installing packages via homebrew -install_brew_package() { - if brew list -1 | grep -q "^$1\$"; then - # Package is installed, upgrade if needed - brew outdated "$1" || brew upgrade "$@" - else - # Package not installed yet, install. - # If there are conflicts, try overwriting the files (these are in /usr/local anyway so it should be ok). - brew install "$@" || brew link --overwrite gcc49 - fi -} - -# Update packages -travis_fold start brew_update -brew update -travis_fold end brew_update - -travis_fold start brew_install_util -# For md5sum -install_brew_package md5sha1sum -# For `timeout' -install_brew_package coreutils - -which cmake &>/dev/null || install_brew_package cmake - -# Install compiler -case "${COMPILER}" in -gcc-4.8) install_brew_package gcc@4.8 ;; -gcc-4.9) install_brew_package gcc@4.9 ;; -gcc-5) install_brew_package gcc@5 ;; -gcc-6) install_brew_package gcc@6 ;; -clang-default) ;; -clang-3.7) install_brew_package llvm@3.7 --with-clang --with-libcxx;; -clang-3.8) install_brew_package llvm@3.8 --with-clang --with-libcxx;; -clang-3.9) install_brew_package llvm@3.9 --with-clang --with-libcxx;; -clang-4.0) install_brew_package llvm --with-clang --with-libcxx;; -*) echo "Compiler not supported: ${COMPILER}. See travis/install_osx.sh"; exit 1 ;; -esac -travis_fold end brew_install_util - - -# Install dependencies -travis_fold start brew_install_dependencies -install_brew_package gmp --c++11 -install_brew_package cln -install_brew_package ginac -install_brew_package boost --c++11 -install_brew_package python -install_brew_package python3 -travis_fold end brew_install_dependencies From d9b020b1bca4b7fe9a04dc54905e2c3af0663014 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 17 May 2018 15:36:54 +0200 Subject: [PATCH 040/147] Refactored sparse model bindings --- lib/stormpy/__init__.py | 97 +++++++++------------ src/mod_storage.cpp | 1 + src/storage/model.cpp | 188 +++++++++++++++++++++------------------- src/storage/model.h | 1 + 4 files changed, 142 insertions(+), 145 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 8a47e13..883cc2c 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -22,15 +22,46 @@ except ImportError: core._set_up("") +def _convert_sparse_model(model, parametric=False): + """ + Convert (parametric) model in sparse representation into model corresponding to exact model type. + :param model: Sparse model. + :param parametric: Flag indicating if the model is parametric. + :return: Model corresponding to exact model type. + """ + if parametric: + assert model.supports_parameters + if model.model_type == ModelType.DTMC: + return model._as_sparse_pdtmc() + elif model.model_type == ModelType.MDP: + return model._as_sparse_pmdp() + elif model.model_type == ModelType.CTMC: + return model._as_sparse_pctmc() + elif model.model_type == ModelType.MA: + return model._as_sparse_pma() + else: + raise StormError("Not supported parametric model constructed") + else: + assert not model.supports_parameters + if model.model_type == ModelType.DTMC: + return model._as_sparse_dtmc() + elif model.model_type == ModelType.MDP: + return model._as_sparse_mdp() + elif model.model_type == ModelType.CTMC: + return model._as_sparse_ctmc() + elif model.model_type == ModelType.MA: + return model._as_sparse_ma() + else: + raise StormError("Not supported non-parametric model constructed") + def build_model(symbolic_description, properties=None): """ - Build a model from a symbolic description. + Build a model in sparse representation from a symbolic description. :param symbolic_description: Symbolic model description to translate into a model. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. :return: Model in sparse representation. - :rtype: SparseDtmc or SparseMdp """ if not symbolic_description.undefined_constants_are_graph_preserving: raise StormError("Program still contains undefined constants") @@ -40,91 +71,49 @@ def build_model(symbolic_description, properties=None): intermediate = core._build_sparse_model_from_prism_program(symbolic_description, formulae) else: intermediate = core._build_sparse_model_from_prism_program(symbolic_description) - assert not intermediate.supports_parameters - if intermediate.model_type == ModelType.DTMC: - return intermediate._as_dtmc() - elif intermediate.model_type == ModelType.MDP: - return intermediate._as_mdp() - elif intermediate.model_type == ModelType.CTMC: - return intermediate._as_ctmc() - elif intermediate.model_type == ModelType.MA: - return intermediate._as_ma() - else: - raise StormError("Not supported non-parametric model constructed") + return _convert_sparse_model(intermediate, parametric=False) def build_parametric_model(symbolic_description, properties=None): """ - Build a parametric model from a symbolic description. + Build a parametric model in sparse representation from a symbolic description. :param symbolic_description: Symbolic model description to translate into a model. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. :return: Parametric model in sparse representation. - :rtype: SparseParametricDtmc or SparseParametricMdp """ if not symbolic_description.undefined_constants_are_graph_preserving: raise StormError("Program still contains undefined constants") if properties: formulae = [prop.raw_formula for prop in properties] + intermediate = core._build_sparse_parametric_model_from_prism_program(symbolic_description, formulae) else: - formulae = [] - intermediate = core._build_sparse_parametric_model_from_prism_program(symbolic_description, formulae) - assert intermediate.supports_parameters - if intermediate.model_type == ModelType.DTMC: - return intermediate._as_pdtmc() - elif intermediate.model_type == ModelType.MDP: - return intermediate._as_pmdp() - elif intermediate.model_type == ModelType.CTMC: - return intermediate._as_pctmc() - elif intermediate.model_type == ModelType.MA: - return intermediate._as_pma() - else: - raise StormError("Not supported parametric model constructed") + intermediate = core._build_sparse_parametric_model_from_prism_program(symbolic_description) + return _convert_sparse_model(intermediate, parametric=True) def build_model_from_drn(file): """ - Build a model from the explicit DRN representation. + Build a model in sparse representation from the explicit DRN representation. :param String file: DRN file containing the model. :return: Model in sparse representation. - :rtype: SparseDtmc or SparseMdp or SparseCTMC or SparseMA """ intermediate = core._build_sparse_model_from_drn(file) - assert not intermediate.supports_parameters - if intermediate.model_type == ModelType.DTMC: - return intermediate._as_dtmc() - elif intermediate.model_type == ModelType.MDP: - return intermediate._as_mdp() - elif intermediate.model_type == ModelType.CTMC: - return intermediate._as_ctmc() - elif intermediate.model_type == ModelType.MA: - return intermediate._as_ma() - else: - raise StormError("Not supported non-parametric model constructed") + return _convert_sparse_model(intermediate, parametric=False) def build_parametric_model_from_drn(file): """ - Build a parametric model from the explicit DRN representation. + Build a parametric model in sparse representation from the explicit DRN representation. :param String file: DRN file containing the model. :return: Parametric model in sparse representation. - :rtype: SparseParametricDtmc or SparseParametricMdp or SparseParametricCTMC or SparseParametricMA """ intermediate = core._build_sparse_parametric_model_from_drn(file) - assert intermediate.supports_parameters - if intermediate.model_type == ModelType.DTMC: - return intermediate._as_pdtmc() - elif intermediate.model_type == ModelType.MDP: - return intermediate._as_pmdp() - elif intermediate.model_type == ModelType.CTMC: - return intermediate._as_pctmc() - elif intermediate.model_type == ModelType.MA: - return intermediate._as_pma() - else: - raise StormError("Not supported parametric model constructed") + return _convert_sparse_model(intermediate, parametric=True) + def perform_bisimulation(model, properties, bisimulation_type): diff --git a/src/mod_storage.cpp b/src/mod_storage.cpp index 4fd3cbd..9907378 100644 --- a/src/mod_storage.cpp +++ b/src/mod_storage.cpp @@ -20,6 +20,7 @@ PYBIND11_MODULE(storage, m) { define_bitvector(m); define_model(m); + define_sparse_model(m); define_sparse_matrix(m); define_state(m); define_prism(m); diff --git a/src/storage/model.cpp b/src/storage/model.cpp index ec3d43f..9d0993b 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -13,22 +13,22 @@ #include #include +// Typedefs +using RationalFunction = storm::RationalFunction; +using state_type = storm::storage::sparse::state_type; +template using SparseRewardModel = storm::models::sparse::StandardRewardModel; using ModelBase = storm::models::ModelBase; -using state_type = storm::storage::sparse::state_type; -using RationalFunction = storm::RationalFunction; -using RationalFunctionVariable = storm::RationalFunctionVariable; -template using Model = storm::models::sparse::Model; -template using Dtmc = storm::models::sparse::Dtmc; -template using Mdp = storm::models::sparse::Mdp; -template using Ctmc = storm::models::sparse::Ctmc; -template using MarkovAutomaton = storm::models::sparse::MarkovAutomaton; -template using SparseMatrix = storm::storage::SparseMatrix; -template using RewardModel = storm::models::sparse::StandardRewardModel; +template using SparseModel = storm::models::sparse::Model; +template using SparseDtmc = storm::models::sparse::Dtmc; +template using SparseMdp = storm::models::sparse::Mdp; +template using SparseCtmc = storm::models::sparse::Ctmc; +template using SparseMarkovAutomaton = storm::models::sparse::MarkovAutomaton; + // Thin wrapper for getting initial states template -std::vector getInitialStates(Model const& model) { +std::vector getInitialStates(SparseModel const& model) { std::vector initialStates; for (auto entry : model.getInitialStates()) { initialStates.push_back(entry); @@ -38,28 +38,28 @@ std::vector getInitialStates(Model const& model) { // Thin wrapper for getting transition matrix template -SparseMatrix& getTransitionMatrix(Model& model) { +storm::storage::SparseMatrix& getTransitionMatrix(SparseModel& model) { return model.getTransitionMatrix(); } template -storm::storage::SparseMatrix getBackwardTransitionMatrix(storm::models::sparse::Model& model) { - return model.getBackwardTransitions(); +storm::storage::SparseMatrix getBackwardTransitionMatrix(SparseModel const& model) { + return std::move(model.getBackwardTransitions()); } // requires pycarl.Variable -std::set probabilityVariables(Model const& model) { +std::set probabilityVariables(SparseModel const& model) { return storm::models::sparse::getProbabilityParameters(model); } -std::set rewardVariables(Model const& model) { +std::set rewardVariables(SparseModel const& model) { return storm::models::sparse::getRewardParameters(model); } template -std::function const&)> getModelInfoPrinter(std::string name = "Model") { +std::function const&)> getModelInfoPrinter(std::string name = "Model") { // look, C++ has lambdas and stuff! - return [name](Model const& model) { + return [name](storm::models::Model const& model) { std::stringstream ss; model.printModelInformationToStream(ss); @@ -75,11 +75,12 @@ std::function const&)> getModelInfoPrinter(std::st } template -storm::models::sparse::StateLabeling& getLabeling(storm::models::sparse::Model& model) { +storm::models::sparse::StateLabeling& getLabeling(SparseModel& model) { return model.getStateLabeling(); } -// Define python bindings + +// Bindings for general models void define_model(py::module& m) { // ModelType @@ -98,116 +99,121 @@ void define_model(py::module& m) { .def_property_readonly("supports_parameters", &ModelBase::supportsParameters, "Flag whether model supports parameters") .def_property_readonly("has_parameters", &ModelBase::hasParameters, "Flag whether model has parameters") .def_property_readonly("is_exact", &ModelBase::isExact, "Flag whether model is exact") - .def("_as_dtmc", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as DTMC") - .def("_as_pdtmc", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as pDTMC") - .def("_as_mdp", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as MDP") - .def("_as_pmdp", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as pMDP") - .def("_as_ctmc", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as CTMC") - .def("_as_pctmc", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as pCTMC") - .def("_as_ma", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as MA") - .def("_as_pma", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as pMA") + .def("_as_sparse_dtmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse DTMC") + .def("_as_sparse_pdtmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse pDTMC") + .def("_as_sparse_mdp", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse MDP") + .def("_as_sparse_pmdp", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse pMDP") + .def("_as_sparse_ctmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse CTMC") + .def("_as_sparse_pctmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse pCTMC") + .def("_as_sparse_ma", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse MA") + .def("_as_sparse_pma", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse pMA") + .def("_as_symbolic_dtmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic DTMC") ; +} + + +// Bindings for sparse models +void define_sparse_model(py::module& m) { + // Models - py::class_, std::shared_ptr>> model(m, "_SparseModel", "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix", modelBase); + py::class_, std::shared_ptr>, ModelBase> model(m, "_SparseModel", "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix"); model.def_property_readonly("labeling", &getLabeling, "Labels") - .def("labels_state", &Model::getLabelsOfState, py::arg("state"), "Get labels of state") + .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") .def_property_readonly("initial_states", &getInitialStates, "Initial states") - .def_property_readonly("states", [](Model& model) { + .def_property_readonly("states", [](SparseModel& model) { return SparseModelStates(model); }, "Get states") - .def_property_readonly("reward_models", [](Model& model) {return model.getRewardModels(); }, "Reward models") + .def_property_readonly("reward_models", [](SparseModel& model) {return model.getRewardModels(); }, "Reward models") .def_property_readonly("transition_matrix", &getTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Transition matrix") .def_property_readonly("backward_transition_matrix", &getBackwardTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") - .def("reduce_to_state_based_rewards", &Model::reduceToStateBasedRewards) + .def("reduce_to_state_based_rewards", &SparseModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter()) ; - py::class_, std::shared_ptr>>(m, "SparseDtmc", "DTMC in sparse representation", model) + py::class_, std::shared_ptr>>(m, "SparseDtmc", "DTMC in sparse representation", model) .def("__str__", getModelInfoPrinter("DTMC")) ; - py::class_, std::shared_ptr>>(m, "SparseMdp", "MDP in sparse representation", model) + py::class_, std::shared_ptr>>(m, "SparseMdp", "MDP in sparse representation", model) .def("__str__", getModelInfoPrinter("MDP")) ; - py::class_, std::shared_ptr>>(m, "SparseCtmc", "CTMC in sparse representation", model) + py::class_, std::shared_ptr>>(m, "SparseCtmc", "CTMC in sparse representation", model) .def("__str__", getModelInfoPrinter("CTMC")) ; - py::class_, std::shared_ptr>>(m, "SparseMA", "MA in sparse representation", model) + py::class_, std::shared_ptr>>(m, "SparseMA", "MA in sparse representation", model) .def("__str__", getModelInfoPrinter("MA")) ; - py::class_>(m, "SparseRewardModel", "Reward structure for sparse models") - .def_property_readonly("has_state_rewards", &RewardModel::hasStateRewards) - .def_property_readonly("has_state_action_rewards", &RewardModel::hasStateActionRewards) - .def_property_readonly("has_transition_rewards", &RewardModel::hasTransitionRewards) - .def_property_readonly("transition_rewards", [](RewardModel& rewardModel) {return rewardModel.getTransitionRewardMatrix();}) - .def_property_readonly("state_rewards", [](RewardModel& rewardModel) {return rewardModel.getStateRewardVector();}) - .def("get_state_reward", [](RewardModel& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);}) - .def("get_state_action_reward", [](RewardModel& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);}) - .def_property_readonly("state_action_rewards", [](RewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) - .def("reduce_to_state_based_rewards", [](RewardModel& rewardModel, SparseMatrix const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);}, py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards") - ; - + py::class_>(m, "SparseRewardModel", "Reward structure for sparse models") + .def_property_readonly("has_state_rewards", &SparseRewardModel::hasStateRewards) + .def_property_readonly("has_state_action_rewards", &SparseRewardModel::hasStateActionRewards) + .def_property_readonly("has_transition_rewards", &SparseRewardModel::hasTransitionRewards) + .def_property_readonly("transition_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getTransitionRewardMatrix();}) + .def_property_readonly("state_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateRewardVector();}) + .def("get_state_reward", [](SparseRewardModel& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);}) + .def("get_state_action_reward", [](SparseRewardModel& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);}) + .def_property_readonly("state_action_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) + .def("reduce_to_state_based_rewards", [](SparseRewardModel& rewardModel, storm::storage::SparseMatrix const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);}, py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards") + ; - py::class_, std::shared_ptr>> modelRatFunc(m, "_SparseParametricModel", "A probabilistic model where transitions are represented by rational functions and saved in a sparse matrix", modelBase); + py::class_, std::shared_ptr>, ModelBase> modelRatFunc(m, "_SparseParametricModel", "A probabilistic model where transitions are represented by rational functions and saved in a sparse matrix"); modelRatFunc.def("collect_probability_parameters", &probabilityVariables, "Collect parameters") .def("collect_reward_parameters", &rewardVariables, "Collect reward parameters") - .def_property_readonly("labeling", &getLabeling, "Labels") - .def("labels_state", &Model::getLabelsOfState, py::arg("state"), "Get labels of state") + .def_property_readonly("labeling", &getLabeling, "Labels") + .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") .def_property_readonly("initial_states", &getInitialStates, "Initial states") - .def_property_readonly("states", [](Model& model) { - return SparseModelStates(model); + .def_property_readonly("states", [](SparseModel& model) { + return SparseModelStates(model); }, "Get states") - .def_property_readonly("reward_models", [](Model const& model) {return model.getRewardModels(); }, "Reward models") + .def_property_readonly("reward_models", [](SparseModel const& model) {return model.getRewardModels(); }, "Reward models") .def_property_readonly("transition_matrix", &getTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Transition matrix") - .def_property_readonly("backward_transition_matrix", &getBackwardTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") - .def("reduce_to_state_based_rewards", &Model::reduceToStateBasedRewards) + .def_property_readonly("backward_transition_matrix", &getBackwardTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") + .def("reduce_to_state_based_rewards", &SparseModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter("ParametricModel")) ; - py::class_, std::shared_ptr>>(m, "SparseParametricDtmc", "pDTMC in sparse representation", modelRatFunc) + py::class_, std::shared_ptr>>(m, "SparseParametricDtmc", "pDTMC in sparse representation", modelRatFunc) .def("__str__", getModelInfoPrinter("ParametricDTMC")) ; - - py::class_, std::shared_ptr>>(m, "SparseParametricMdp", "pMDP in sparse representation", modelRatFunc) + py::class_, std::shared_ptr>>(m, "SparseParametricMdp", "pMDP in sparse representation", modelRatFunc) .def("__str__", getModelInfoPrinter("ParametricMDP")) ; - - py::class_, std::shared_ptr>>(m, "SparseParametricCtmc", "pCTMC in sparse representation", modelRatFunc) + py::class_, std::shared_ptr>>(m, "SparseParametricCtmc", "pCTMC in sparse representation", modelRatFunc) .def("__str__", getModelInfoPrinter("ParametricCTMC")) ; - - py::class_, std::shared_ptr>>(m, "SparseParametricMA", "pMA in sparse representation", modelRatFunc) + py::class_, std::shared_ptr>>(m, "SparseParametricMA", "pMA in sparse representation", modelRatFunc) .def("__str__", getModelInfoPrinter("ParametricMA")) ; - py::class_>(m, "SparseParametricRewardModel", "Reward structure for parametric sparse models") - .def_property_readonly("has_state_rewards", &RewardModel::hasStateRewards) - .def_property_readonly("has_state_action_rewards", &RewardModel::hasStateActionRewards) - .def_property_readonly("has_transition_rewards", &RewardModel::hasTransitionRewards) - .def_property_readonly("transition_rewards", [](RewardModel& rewardModel) {return rewardModel.getTransitionRewardMatrix();}) - .def_property_readonly("state_rewards", [](RewardModel& rewardModel) {return rewardModel.getStateRewardVector();}) - .def("get_state_reward", [](RewardModel& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);}) - .def("get_state_action_reward", [](RewardModel& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);}) - - .def_property_readonly("state_action_rewards", [](RewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) - .def("reduce_to_state_based_rewards", [](RewardModel& rewardModel, SparseMatrix const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);}, py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards") + py::class_>(m, "SparseParametricRewardModel", "Reward structure for parametric sparse models") + .def_property_readonly("has_state_rewards", &SparseRewardModel::hasStateRewards) + .def_property_readonly("has_state_action_rewards", &SparseRewardModel::hasStateActionRewards) + .def_property_readonly("has_transition_rewards", &SparseRewardModel::hasTransitionRewards) + .def_property_readonly("transition_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getTransitionRewardMatrix();}) + .def_property_readonly("state_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateRewardVector();}) + .def("get_state_reward", [](SparseRewardModel& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);}) + .def("get_state_action_reward", [](SparseRewardModel& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);}) + + .def_property_readonly("state_action_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) + .def("reduce_to_state_based_rewards", [](SparseRewardModel& rewardModel, storm::storage::SparseMatrix const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);}, py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards") ; } diff --git a/src/storage/model.h b/src/storage/model.h index c779f26..ac91a56 100644 --- a/src/storage/model.h +++ b/src/storage/model.h @@ -4,5 +4,6 @@ #include "common.h" void define_model(py::module& m); +void define_sparse_model(py::module& m); #endif /* PYTHON_STORAGE_MODEL_H_ */ From 0072a3fc980c54b61920b367ddad03e7bd6256ae Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 17 May 2018 17:35:48 +0200 Subject: [PATCH 041/147] Refactoring for sparse models --- src/storage/model.cpp | 36 ++++++++++++++---------------------- src/storage/model.h | 4 +--- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/storage/model.cpp b/src/storage/model.cpp index 9d0993b..c185ae6 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -15,38 +15,32 @@ // Typedefs using RationalFunction = storm::RationalFunction; -using state_type = storm::storage::sparse::state_type; -template using SparseRewardModel = storm::models::sparse::StandardRewardModel; - using ModelBase = storm::models::ModelBase; + template using SparseModel = storm::models::sparse::Model; template using SparseDtmc = storm::models::sparse::Dtmc; template using SparseMdp = storm::models::sparse::Mdp; template using SparseCtmc = storm::models::sparse::Ctmc; template using SparseMarkovAutomaton = storm::models::sparse::MarkovAutomaton; +template using SparseRewardModel = storm::models::sparse::StandardRewardModel; + -// Thin wrapper for getting initial states +// Thin wrappers template -std::vector getInitialStates(SparseModel const& model) { - std::vector initialStates; +std::vector getSparseInitialStates(SparseModel const& model) { + std::vector initialStates; for (auto entry : model.getInitialStates()) { initialStates.push_back(entry); } return initialStates; } -// Thin wrapper for getting transition matrix template storm::storage::SparseMatrix& getTransitionMatrix(SparseModel& model) { return model.getTransitionMatrix(); } -template -storm::storage::SparseMatrix getBackwardTransitionMatrix(SparseModel const& model) { - return std::move(model.getBackwardTransitions()); -} - // requires pycarl.Variable std::set probabilityVariables(SparseModel const& model) { return storm::models::sparse::getProbabilityParameters(model); @@ -123,9 +117,7 @@ void define_model(py::module& m) { .def("_as_sparse_pma", [](ModelBase &modelbase) { return modelbase.as>(); }, "Get model as sparse pMA") - .def("_as_symbolic_dtmc", [](ModelBase &modelbase) { - return modelbase.as>(); - }, "Get model as symbolic DTMC") + ; } @@ -133,18 +125,17 @@ void define_model(py::module& m) { // Bindings for sparse models void define_sparse_model(py::module& m) { - - // Models + // Models with double numbers py::class_, std::shared_ptr>, ModelBase> model(m, "_SparseModel", "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix"); model.def_property_readonly("labeling", &getLabeling, "Labels") .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") - .def_property_readonly("initial_states", &getInitialStates, "Initial states") + .def_property_readonly("initial_states", &getSparseInitialStates, "Initial states") .def_property_readonly("states", [](SparseModel& model) { return SparseModelStates(model); }, "Get states") .def_property_readonly("reward_models", [](SparseModel& model) {return model.getRewardModels(); }, "Reward models") .def_property_readonly("transition_matrix", &getTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Transition matrix") - .def_property_readonly("backward_transition_matrix", &getBackwardTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") + .def_property_readonly("backward_transition_matrix", &SparseModel::getBackwardTransitions, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") .def("reduce_to_state_based_rewards", &SparseModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter()) ; @@ -169,23 +160,24 @@ void define_sparse_model(py::module& m) { .def_property_readonly("state_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateRewardVector();}) .def("get_state_reward", [](SparseRewardModel& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);}) .def("get_state_action_reward", [](SparseRewardModel& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);}) - .def_property_readonly("state_action_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) + .def_property_readonly("state_action_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) .def("reduce_to_state_based_rewards", [](SparseRewardModel& rewardModel, storm::storage::SparseMatrix const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);}, py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards") ; + // Parametric models py::class_, std::shared_ptr>, ModelBase> modelRatFunc(m, "_SparseParametricModel", "A probabilistic model where transitions are represented by rational functions and saved in a sparse matrix"); modelRatFunc.def("collect_probability_parameters", &probabilityVariables, "Collect parameters") .def("collect_reward_parameters", &rewardVariables, "Collect reward parameters") .def_property_readonly("labeling", &getLabeling, "Labels") .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") - .def_property_readonly("initial_states", &getInitialStates, "Initial states") + .def_property_readonly("initial_states", &getSparseInitialStates, "Initial states") .def_property_readonly("states", [](SparseModel& model) { return SparseModelStates(model); }, "Get states") .def_property_readonly("reward_models", [](SparseModel const& model) {return model.getRewardModels(); }, "Reward models") .def_property_readonly("transition_matrix", &getTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Transition matrix") - .def_property_readonly("backward_transition_matrix", &getBackwardTransitionMatrix, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") + .def_property_readonly("backward_transition_matrix", &SparseModel::getBackwardTransitions, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") .def("reduce_to_state_based_rewards", &SparseModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter("ParametricModel")) ; diff --git a/src/storage/model.h b/src/storage/model.h index ac91a56..50f4c31 100644 --- a/src/storage/model.h +++ b/src/storage/model.h @@ -1,9 +1,7 @@ -#ifndef PYTHON_STORAGE_MODEL_H_ -#define PYTHON_STORAGE_MODEL_H_ +#pragma once #include "common.h" void define_model(py::module& m); void define_sparse_model(py::module& m); -#endif /* PYTHON_STORAGE_MODEL_H_ */ From 21ecfacc3b69e2f3a6ec5c7c30fe5a6626561d2c Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 17 May 2018 17:49:44 +0200 Subject: [PATCH 042/147] Bindings for symbolic models using Sylvan --- src/mod_storage.cpp | 3 ++ src/storage/model.cpp | 105 +++++++++++++++++++++++++++++++++++++++++- src/storage/model.h | 3 ++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/mod_storage.cpp b/src/mod_storage.cpp index 9907378..a8d3089 100644 --- a/src/mod_storage.cpp +++ b/src/mod_storage.cpp @@ -10,6 +10,8 @@ #include "storage/labeling.h" #include "storage/expressions.h" +#include "storm/storage/dd/DdType.h" + PYBIND11_MODULE(storage, m) { m.doc() = "Data structures in Storm"; @@ -22,6 +24,7 @@ PYBIND11_MODULE(storage, m) { define_model(m); define_sparse_model(m); define_sparse_matrix(m); + define_symbolic_model(m, "Sylvan"); define_state(m); define_prism(m); define_labeling(m); diff --git a/src/storage/model.cpp b/src/storage/model.cpp index c185ae6..f2f49a1 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -8,6 +8,12 @@ #include "storm/models/sparse/Ctmc.h" #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/models/sparse/StandardRewardModel.h" +#include "storm/models/symbolic/Model.h" +#include "storm/models/symbolic/Dtmc.h" +#include "storm/models/symbolic/Mdp.h" +#include "storm/models/symbolic/Ctmc.h" +#include "storm/models/symbolic/MarkovAutomaton.h" +#include "storm/models/symbolic/StandardRewardModel.h" #include #include @@ -24,6 +30,13 @@ template using SparseCtmc = storm::models::sparse::Ctmc using SparseMarkovAutomaton = storm::models::sparse::MarkovAutomaton; template using SparseRewardModel = storm::models::sparse::StandardRewardModel; +template using SymbolicModel = storm::models::symbolic::Model; +template using SymbolicDtmc = storm::models::symbolic::Dtmc; +template using SymbolicMdp = storm::models::symbolic::Mdp; +template using SymbolicCtmc = storm::models::symbolic::Ctmc; +template using SymbolicMarkovAutomaton = storm::models::symbolic::MarkovAutomaton; +template using SymbolicRewardModel = storm::models::symbolic::StandardRewardModel; + // Thin wrappers @@ -117,7 +130,30 @@ void define_model(py::module& m) { .def("_as_sparse_pma", [](ModelBase &modelbase) { return modelbase.as>(); }, "Get model as sparse pMA") - + .def("_as_symbolic_dtmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic DTMC") + .def("_as_symbolic_pdtmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic pDTMC") + .def("_as_symbolic_mdp", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic MDP") + .def("_as_symbolic_pmdp", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic pMDP") + .def("_as_symbolic_ctmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic CTMC") + .def("_as_symbolic_pctmc", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic pCTMC") + .def("_as_symbolic_ma", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic MA") + .def("_as_symbolic_pma", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as symbolic pMA") ; } @@ -210,3 +246,70 @@ void define_sparse_model(py::module& m) { } + +// Bindings for symbolic models +template +void define_symbolic_model(py::module& m, std::string vt_suffix) { + + // Set class names + std::string prefixClassName = "Symbolic" + vt_suffix; + std::string prefixParametricClassName = "Symbolic" + vt_suffix + "Parametric"; + + + // Models with double numbers + py::class_, std::shared_ptr>, ModelBase> model(m, ("_"+prefixClassName+"Model").c_str(), "A probabilistic model where transitions are represented by doubles and saved in a symbolic representation"); + model.def_property_readonly("reward_models", [](SymbolicModel& model) {return model.getRewardModels(); }, "Reward models") + .def("reduce_to_state_based_rewards", &SymbolicModel::reduceToStateBasedRewards) + .def("__str__", getModelInfoPrinter()) + ; + py::class_, std::shared_ptr>>(m, (prefixClassName+"Dtmc").c_str(), "DTMC in symbolic representation", model) + .def("__str__", getModelInfoPrinter("DTMC")) + ; + py::class_, std::shared_ptr>>(m, (prefixClassName+"Mdp").c_str(), "MDP in symbolic representation", model) + .def("__str__", getModelInfoPrinter("MDP")) + ; + py::class_, std::shared_ptr>>(m, (prefixClassName+"Ctmc").c_str(), "CTMC in symbolic representation", model) + .def("__str__", getModelInfoPrinter("CTMC")) + ; + py::class_, std::shared_ptr>>(m, (prefixClassName+"MA").c_str(), "MA in symbolic representation", model) + .def("__str__", getModelInfoPrinter("MA")) + ; + + py::class_>(m, (prefixClassName+"RewardModel").c_str(), "Reward structure for symbolic models") + .def_property_readonly("has_state_rewards", &SymbolicRewardModel::hasStateRewards) + .def_property_readonly("has_state_action_rewards", &SymbolicRewardModel::hasStateActionRewards) + .def_property_readonly("has_transition_rewards", &SymbolicRewardModel::hasTransitionRewards) + ; + + + // Parametric models + py::class_, std::shared_ptr>, ModelBase> modelRatFunc(m, ("_"+prefixParametricClassName+"Model").c_str(), "A probabilistic model where transitions are represented by rational functions and saved in a symbolic representation"); + modelRatFunc.def("collect_probability_parameters", &probabilityVariables, "Collect parameters") + .def("collect_reward_parameters", &rewardVariables, "Collect reward parameters") + .def_property_readonly("reward_models", [](SymbolicModel const& model) {return model.getRewardModels(); }, "Reward models") + .def("reduce_to_state_based_rewards", &SymbolicModel::reduceToStateBasedRewards) + .def("__str__", getModelInfoPrinter("ParametricModel")) + ; + + py::class_, std::shared_ptr>>(m, (prefixParametricClassName+"Dtmc").c_str(), "pDTMC in symbolic representation", modelRatFunc) + .def("__str__", getModelInfoPrinter("ParametricDTMC")) + ; + py::class_, std::shared_ptr>>(m, (prefixParametricClassName+"Mdp").c_str(), "pMDP in symbolic representation", modelRatFunc) + .def("__str__", getModelInfoPrinter("ParametricMDP")) + ; + py::class_, std::shared_ptr>>(m, (prefixParametricClassName+"Ctmc").c_str(), "pCTMC in symbolic representation", modelRatFunc) + .def("__str__", getModelInfoPrinter("ParametricCTMC")) + ; + py::class_, std::shared_ptr>>(m, (prefixParametricClassName+"MA").c_str(), "pMA in symbolic representation", modelRatFunc) + .def("__str__", getModelInfoPrinter("ParametricMA")) + ; + + py::class_>(m, (prefixParametricClassName+"RewardModel").c_str(), "Reward structure for parametric symbolic models") + .def_property_readonly("has_state_rewards", &SymbolicRewardModel::hasStateRewards) + .def_property_readonly("has_state_action_rewards", &SymbolicRewardModel::hasStateActionRewards) + .def_property_readonly("has_transition_rewards", &SymbolicRewardModel::hasTransitionRewards) + ; + +} + +template void define_symbolic_model(py::module& m, std::string vt_suffix); diff --git a/src/storage/model.h b/src/storage/model.h index 50f4c31..7643d39 100644 --- a/src/storage/model.h +++ b/src/storage/model.h @@ -1,7 +1,10 @@ #pragma once #include "common.h" +#include "storm/storage/dd/DdType.h" void define_model(py::module& m); void define_sparse_model(py::module& m); +template +void define_symbolic_model(py::module& m, std::string vt_suffix); From cfb6dfbf2f9b7bcf7ead1bc5c1ef220b8ae7f7be Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 11:53:00 +0200 Subject: [PATCH 043/147] Better naming for sparse model building --- lib/stormpy/__init__.py | 10 +++++----- src/core/core.cpp | 16 ++++++++-------- src/core/core.h | 7 ++----- tests/storage/test_model.py | 14 ++------------ 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 883cc2c..0720ee3 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -22,6 +22,7 @@ except ImportError: core._set_up("") + def _convert_sparse_model(model, parametric=False): """ Convert (parametric) model in sparse representation into model corresponding to exact model type. @@ -68,9 +69,9 @@ def build_model(symbolic_description, properties=None): if properties: formulae = [prop.raw_formula for prop in properties] - intermediate = core._build_sparse_model_from_prism_program(symbolic_description, formulae) + intermediate = core._build_sparse_model_from_symbolic_description(symbolic_description, formulae) else: - intermediate = core._build_sparse_model_from_prism_program(symbolic_description) + intermediate = core._build_sparse_model_from_symbolic_description(symbolic_description) return _convert_sparse_model(intermediate, parametric=False) @@ -87,9 +88,9 @@ def build_parametric_model(symbolic_description, properties=None): if properties: formulae = [prop.raw_formula for prop in properties] - intermediate = core._build_sparse_parametric_model_from_prism_program(symbolic_description, formulae) + intermediate = core._build_sparse_parametric_model_from_symbolic_description(symbolic_description, formulae) else: - intermediate = core._build_sparse_parametric_model_from_prism_program(symbolic_description) + intermediate = core._build_sparse_parametric_model_from_symbolic_description(symbolic_description) return _convert_sparse_model(intermediate, parametric=True) @@ -115,7 +116,6 @@ def build_parametric_model_from_drn(file): return _convert_sparse_model(intermediate, parametric=True) - def perform_bisimulation(model, properties, bisimulation_type): """ Perform bisimulation on model. diff --git a/src/core/core.cpp b/src/core/core.cpp index b4b5de6..e0e4af4 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -46,9 +46,9 @@ void define_parse(py::module& m) { ; } -// Thin wrapper for model building using one formula as argument +// Thin wrapper for model building using sparse representation template -std::shared_ptr buildSparseModel(storm::storage::SymbolicModelDescription const& modelDescription, std::vector> const& formulas, bool jit = false, bool doctor = false) { +std::shared_ptr> buildSparseModel(storm::storage::SymbolicModelDescription const& modelDescription, std::vector> const& formulas, bool jit = false, bool doctor = false) { if (formulas.empty()) { // Build all labels and rewards storm::builder::BuilderOptions options(true, true); @@ -61,8 +61,8 @@ std::shared_ptr buildSparseModel(storm::storage::Symbo void define_build(py::module& m) { // Build model - m.def("_build_sparse_model_from_prism_program", &buildSparseModel, "Build the model", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); - m.def("_build_sparse_parametric_model_from_prism_program", &buildSparseModel, "Build the parametric model", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); + m.def("_build_sparse_model_from_symbolic_description", &buildSparseModel, "Build the model in sparse representation", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); + m.def("_build_sparse_parametric_model_from_symbolic_description", &buildSparseModel, "Build the parametric model in sparse representation", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); m.def("_build_sparse_model_from_drn", &storm::api::buildExplicitDRNModel, "Build the model from DRN", py::arg("file")); m.def("_build_sparse_parametric_model_from_drn", &storm::api::buildExplicitDRNModel, "Build the parametric model from DRN", py::arg("file")); m.def("build_sparse_model_from_explicit", &storm::api::buildExplicitModel, "Build the model model from explicit input", py::arg("transition_file"), py::arg("labeling_file"), py::arg("state_reward_file") = "", py::arg("transition_reward_file") = "", py::arg("choice_labeling_file") = ""); @@ -71,15 +71,15 @@ void define_build(py::module& m) { void define_optimality_type(py::module& m) { py::enum_(m, "OptimizationDirection") - .value("Minimize", storm::solver::OptimizationDirection::Minimize) - .value("Maximize", storm::solver::OptimizationDirection::Maximize) - ; + .value("Minimize", storm::solver::OptimizationDirection::Minimize) + .value("Maximize", storm::solver::OptimizationDirection::Maximize) + ; } // Thin wrapper for exporting model template void exportDRN(std::shared_ptr> model, std::string const& file) { - std::ofstream stream; + std::ofstream stream; storm::utility::openFile(file, stream); storm::exporter::explicitExportSparseModel(stream, model, {}); storm::utility::closeFile(stream); diff --git a/src/core/core.h b/src/core/core.h index 51bc696..cf5a0f9 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -1,5 +1,4 @@ -#ifndef PYTHON_CORE_CORE_H_ -#define PYTHON_CORE_CORE_H_ +#pragma once #include "common.h" @@ -7,6 +6,4 @@ void define_core(py::module& m); void define_parse(py::module& m); void define_build(py::module& m); void define_export(py::module& m); -void define_optimality_type(py::module& m); - -#endif /* PYTHON_CORE_CORE_H_ */ +void define_optimality_type(py::module& m); \ No newline at end of file diff --git a/tests/storage/test_model.py b/tests/storage/test_model.py index d3c4b90..12b8326 100644 --- a/tests/storage/test_model.py +++ b/tests/storage/test_model.py @@ -4,7 +4,7 @@ from helpers.helper import get_example_path import pytest -class TestModel: +class TestSparseModel: def test_build_dtmc_from_prism_program(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) model = stormpy.build_model(program) @@ -26,7 +26,7 @@ class TestModel: assert not model.supports_parameters assert type(model) is stormpy.SparseDtmc - def test_build_dtmc_from_prism_program_formulas(self): + def test_build_dtmc_from_prism_program_reward_formulas(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) prop = "R=? [F \"done\"]" properties = stormpy.parse_properties_for_prism_program(prop, program, None) @@ -65,16 +65,6 @@ class TestModel: assert not model.supports_parameters assert type(model) is stormpy.SparseDtmc - def test_build_dtmc(self): - program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) - formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"one\" ]", program) - model = stormpy.build_model(program, formulas) - assert model.nr_states == 13 - assert model.nr_transitions == 20 - assert model.model_type == stormpy.ModelType.DTMC - assert not model.supports_parameters - assert type(model) is stormpy.SparseDtmc - def test_build_dtmc_with_undefined_constants(self): jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "brp.jani")) assert jani_model.has_undefined_constants From f5a014ed5e082684d03a2a2a965f8e017a7d4eec Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 11:53:40 +0200 Subject: [PATCH 044/147] Bindings for symbolic model building --- lib/stormpy/__init__.py | 71 +++++++++++++++++++++++++++++++++++++++++ src/core/core.cpp | 17 ++++++++++ 2 files changed, 88 insertions(+) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 0720ee3..161e459 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -56,6 +56,39 @@ def _convert_sparse_model(model, parametric=False): raise StormError("Not supported non-parametric model constructed") +def _convert_symbolic_model(model, parametric=False): + """ + Convert (parametric) model in symbolic representation into model corresponding to exact model type. + :param model: Symbolic model. + :param parametric: Flag indicating if the model is parametric. + :return: Model corresponding to exact model type. + """ + if parametric: + assert model.supports_parameters + if model.model_type == ModelType.DTMC: + return model._as_symbolic_pdtmc() + elif model.model_type == ModelType.MDP: + return model._as_symbolic_pmdp() + elif model.model_type == ModelType.CTMC: + return model._as_symbolic_pctmc() + elif model.model_type == ModelType.MA: + return model._as_symbolic_pma() + else: + raise StormError("Not supported parametric model constructed") + else: + assert not model.supports_parameters + if model.model_type == ModelType.DTMC: + return model._as_symbolic_dtmc() + elif model.model_type == ModelType.MDP: + return model._as_symbolic_mdp() + elif model.model_type == ModelType.CTMC: + return model._as_symbolic_ctmc() + elif model.model_type == ModelType.MA: + return model._as_symbolic_ma() + else: + raise StormError("Not supported non-parametric model constructed") + + def build_model(symbolic_description, properties=None): """ Build a model in sparse representation from a symbolic description. @@ -94,6 +127,44 @@ def build_parametric_model(symbolic_description, properties=None): return _convert_sparse_model(intermediate, parametric=True) +def build_symbolic_model(symbolic_description, properties=None): + """ + Build a model in symbolic representation from a symbolic description. + + :param symbolic_description: Symbolic model description to translate into a model. + :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. + :return: Model in symbolic representation. + """ + if not symbolic_description.undefined_constants_are_graph_preserving: + raise StormError("Program still contains undefined constants") + + if properties: + formulae = [prop.raw_formula for prop in properties] + intermediate = core._build_symbolic_model_from_symbolic_description(symbolic_description, formulae) + else: + intermediate = core._build_symbolic_model_from_symbolic_description(symbolic_description) + return _convert_symbolic_model(intermediate, parametric=False) + + +def build_symbolic_parametric_model(symbolic_description, properties=None): + """ + Build a parametric model in symbolic representation from a symbolic description. + + :param symbolic_description: Symbolic model description to translate into a model. + :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. + :return: Parametric model in symbolic representation. + """ + if not symbolic_description.undefined_constants_are_graph_preserving: + raise StormError("Program still contains undefined constants") + + if properties: + formulae = [prop.raw_formula for prop in properties] + intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description, formulae) + else: + intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description) + return _convert_symbolic_model(intermediate, parametric=True) + + def build_model_from_drn(file): """ Build a model in sparse representation from the explicit DRN representation. diff --git a/src/core/core.cpp b/src/core/core.cpp index e0e4af4..eafd9bd 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2,7 +2,10 @@ #include "storm/utility/initialize.h" #include "storm/utility/DirectEncodingExporter.h" #include "storm/storage/ModelFormulasPair.h" +#include "storm/storage/dd/DdType.h" #include "storm/solver/OptimizationDirection.h" +#include "storm/models/symbolic/StandardRewardModel.h" + void define_core(py::module& m) { // Init @@ -59,10 +62,24 @@ std::shared_ptr> buildSparseModel(storm: } } +// Thin wrapper for model building using symbolic representation +template +std::shared_ptr> buildSymbolicModel(storm::storage::SymbolicModelDescription const& modelDescription, std::vector> const& formulas) { + if (formulas.empty()) { + // Build full model + return storm::api::buildSymbolicModel(modelDescription, formulas, true); + } else { + // Only build labels necessary for formulas + return storm::api::buildSymbolicModel(modelDescription, formulas, false); + } +} + void define_build(py::module& m) { // Build model m.def("_build_sparse_model_from_symbolic_description", &buildSparseModel, "Build the model in sparse representation", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); m.def("_build_sparse_parametric_model_from_symbolic_description", &buildSparseModel, "Build the parametric model in sparse representation", py::arg("model_description"), py::arg("formulas") = std::vector>(), py::arg("use_jit") = false, py::arg("doctor") = false); + m.def("_build_symbolic_model_from_symbolic_description", &buildSymbolicModel, "Build the model in symbolic representation", py::arg("model_description"), py::arg("formulas") = std::vector>()); + m.def("_build_symbolic_parametric_model_from_symbolic_description", &buildSymbolicModel, "Build the parametric model in symbolic representation", py::arg("model_description"), py::arg("formulas") = std::vector>()); m.def("_build_sparse_model_from_drn", &storm::api::buildExplicitDRNModel, "Build the model from DRN", py::arg("file")); m.def("_build_sparse_parametric_model_from_drn", &storm::api::buildExplicitDRNModel, "Build the parametric model from DRN", py::arg("file")); m.def("build_sparse_model_from_explicit", &storm::api::buildExplicitModel, "Build the model model from explicit input", py::arg("transition_file"), py::arg("labeling_file"), py::arg("state_reward_file") = "", py::arg("transition_reward_file") = "", py::arg("choice_labeling_file") = ""); From 54433ca8a38f155003e1040d97c25dcf24ee5939 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 11:53:50 +0200 Subject: [PATCH 045/147] Tests for symbolic model building --- tests/storage/test_model.py | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/storage/test_model.py b/tests/storage/test_model.py index 12b8326..2598e3b 100644 --- a/tests/storage/test_model.py +++ b/tests/storage/test_model.py @@ -123,3 +123,90 @@ class TestSparseModel: initial_states = model.initial_states assert len(initial_states) == 1 assert 0 in initial_states + + +class TestSymbolicSylvanModel: + def test_build_dtmc_from_prism_program(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + model = stormpy.build_symbolic_model(program) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + assert model.model_type == stormpy.ModelType.DTMC + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanDtmc + + def test_build_dtmc_from_prism_program_formulas(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + prop = "P=? [F \"one\"]" + properties = stormpy.parse_properties_for_prism_program(prop, program, None) + model = stormpy.build_symbolic_model(program, properties) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + assert model.model_type == stormpy.ModelType.DTMC + assert len(model.reward_models) == 0 + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanDtmc + + def test_build_dtmc_from_prism_program_reward_formulas(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + prop = "R=? [F \"done\"]" + properties = stormpy.parse_properties_for_prism_program(prop, program, None) + model = stormpy.build_symbolic_model(program, properties) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + assert model.model_type == stormpy.ModelType.DTMC + assert len(model.reward_models) == 1 + assert not model.reward_models["coin_flips"].has_state_rewards + assert model.reward_models["coin_flips"].has_state_action_rewards + assert not model.reward_models["coin_flips"].has_transition_rewards + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanDtmc + + def test_reduce_to_state_based_rewards(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + prop = "R=? [F \"done\"]" + properties = stormpy.parse_properties_for_prism_program(prop, program, None) + model = stormpy.build_symbolic_model(program, properties) + model.reduce_to_state_based_rewards() + assert len(model.reward_models) == 1 + assert model.reward_models["coin_flips"].has_state_rewards + assert not model.reward_models["coin_flips"].has_state_action_rewards + assert not model.reward_models["coin_flips"].has_transition_rewards + + def test_build_dtmc_from_jani_model(self): + jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "brp.jani")) + description = stormpy.SymbolicModelDescription(jani_model) + constant_definitions = description.parse_constant_definitions("N=16, MAX=2") + instantiated_jani_model = description.instantiate_constants(constant_definitions).as_jani_model() + model = stormpy.build_symbolic_model(instantiated_jani_model) + assert model.nr_states == 677 + assert model.nr_transitions == 867 + assert model.model_type == stormpy.ModelType.DTMC + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanDtmc + + def test_build_mdp(self): + program = stormpy.parse_prism_program(get_example_path("mdp", "two_dice.nm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"two\" ]", program) + model = stormpy.build_symbolic_model(program, formulas) + assert model.nr_states == 169 + assert model.nr_transitions == 435 + assert model.model_type == stormpy.ModelType.MDP + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanMdp + + def test_build_ctmc(self): + program = stormpy.parse_prism_program(get_example_path("ctmc", "polling2.sm"), True) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F<=3 \"target\" ]", program) + model = stormpy.build_symbolic_model(program, formulas) + assert model.nr_states == 12 + assert model.nr_transitions == 22 + assert model.model_type == stormpy.ModelType.CTMC + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanCtmc + + def test_build_ma(self): + program = stormpy.parse_prism_program(get_example_path("ma", "simple.ma")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F<=2 s=2 ]", program) + with pytest.raises(Exception): + model = stormpy.build_symbolic_model(program, formulas) From 32f468e92caf625bd01071ac57e72c6873278680 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 13:45:52 +0200 Subject: [PATCH 046/147] Added tests for symbolic parametric models --- tests/pars/test_parametric_model.py | 37 ++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/pars/test_parametric_model.py b/tests/pars/test_parametric_model.py index d71886a..76e18c2 100644 --- a/tests/pars/test_parametric_model.py +++ b/tests/pars/test_parametric_model.py @@ -7,7 +7,7 @@ from configurations import pars @pars -class TestParametricModel: +class TestSparseParametricModel: def test_build_parametric_dtmc(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) formulas = stormpy.parse_properties_for_prism_program("P=? [ F s=5 ]", program) @@ -39,3 +39,38 @@ class TestParametricModel: assert model.model_type == stormpy.ModelType.MDP assert model.supports_parameters assert type(model) is stormpy.SparseParametricMdp + + +@pars +class TestSymbolicParametricModel: + def test_build_parametric_dtmc(self): + program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F s=5 ]", program) + model = stormpy.build_symbolic_parametric_model(program, formulas) + assert model.nr_states == 613 + assert model.nr_transitions == 803 + assert model.model_type == stormpy.ModelType.DTMC + assert model.supports_parameters + assert model.has_parameters + assert type(model) is stormpy.SymbolicSylvanParametricDtmc + + def test_build_dtmc_supporting_parameters(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"one\" ]", program) + model = stormpy.build_symbolic_parametric_model(program, formulas) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + assert model.model_type == stormpy.ModelType.DTMC + assert model.supports_parameters + assert not model.has_parameters + assert type(model) is stormpy.SymbolicSylvanParametricDtmc + + def test_build_parametric_mdp(self): + program = stormpy.parse_prism_program(get_example_path("pmdp", "two_dice.nm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"two\" ]", program) + model = stormpy.build_symbolic_parametric_model(program, formulas) + assert model.nr_states == 169 + assert model.nr_transitions == 435 + assert model.model_type == stormpy.ModelType.MDP + assert model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanParametricMdp From 62f3d3630ed07d2843d5439e7cf8fe7e7be34961 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 15:02:13 +0200 Subject: [PATCH 047/147] Bindings for dd and hybrid model checking --- lib/stormpy/__init__.py | 82 +++++++++++++++++++++++++++++++- src/core/modelchecking.cpp | 22 +++++++-- src/core/result.cpp | 28 +++++++---- tests/core/test_modelchecking.py | 21 ++++++++ tests/pars/test_parametric.py | 28 ++++++++++- 5 files changed, 168 insertions(+), 13 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 161e459..1424892 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -93,6 +93,28 @@ def build_model(symbolic_description, properties=None): """ Build a model in sparse representation from a symbolic description. + :param symbolic_description: Symbolic model description to translate into a model. + :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. + :return: Model in sparse representation. + """ + return build_sparse_model(symbolic_description, properties=properties) + + +def build_parametric_model(symbolic_description, properties=None): + """ + Build a parametric model in sparse representation from a symbolic description. + + :param symbolic_description: Symbolic model description to translate into a model. + :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. + :return: Parametric model in sparse representation. + """ + return build_sparse_parametric_model(symbolic_description, properties=properties) + + +def build_sparse_model(symbolic_description, properties=None): + """ + Build a model in sparse representation from a symbolic description. + :param symbolic_description: Symbolic model description to translate into a model. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved. :return: Model in sparse representation. @@ -108,7 +130,7 @@ def build_model(symbolic_description, properties=None): return _convert_sparse_model(intermediate, parametric=False) -def build_parametric_model(symbolic_description, properties=None): +def build_sparse_parametric_model(symbolic_description, properties=None): """ Build a parametric model in sparse representation from a symbolic description. @@ -203,6 +225,20 @@ def perform_bisimulation(model, properties, bisimulation_type): def model_checking(model, property, only_initial_states=False, extract_scheduler=False): + """ + Perform model checking on model for property. + :param model: Model. + :param property: Property to check for. + :param only_initial_states: If True, only results for initial states are computed, otherwise for all states. + :param extract_scheduler: If True, try to extract a scheduler + :return: Model checking result. + :rtype: CheckResult + """ + return check_model_sparse(model, property, only_initial_states=only_initial_states, + extract_scheduler=extract_scheduler) + + +def check_model_sparse(model, property, only_initial_states=False, extract_scheduler=False): """ Perform model checking on model for property. :param model: Model. @@ -227,6 +263,50 @@ def model_checking(model, property, only_initial_states=False, extract_scheduler return core._model_checking_sparse_engine(model, task) +def check_model_dd(model, property, only_initial_states=False): + """ + Perform model checking using dd engine. + :param model: Model. + :param property: Property to check for. + :param only_initial_states: If True, only results for initial states are computed, otherwise for all states. + :return: Model checking result. + :rtype: CheckResult + """ + if isinstance(property, Property): + formula = property.raw_formula + else: + formula = property + + if model.supports_parameters: + task = core.ParametricCheckTask(formula, only_initial_states) + return core._parametric_model_checking_dd_engine(model, task) + else: + task = core.CheckTask(formula, only_initial_states) + return core._model_checking_dd_engine(model, task) + + +def check_model_hybrid(model, property, only_initial_states=False): + """ + Perform model checking using hybrid engine. + :param model: Model. + :param property: Property to check for. + :param only_initial_states: If True, only results for initial states are computed, otherwise for all states. + :return: Model checking result. + :rtype: CheckResult + """ + if isinstance(property, Property): + formula = property.raw_formula + else: + formula = property + + if model.supports_parameters: + task = core.ParametricCheckTask(formula, only_initial_states) + return core._parametric_model_checking_hybrid_engine(model, task) + else: + task = core.CheckTask(formula, only_initial_states) + return core._model_checking_hybrid_engine(model, task) + + def prob01min_states(model, eventually_formula): assert type(eventually_formula) == logic.EventuallyFormula labelform = eventually_formula.subformula diff --git a/src/core/modelchecking.cpp b/src/core/modelchecking.cpp index 703e53c..1084a12 100644 --- a/src/core/modelchecking.cpp +++ b/src/core/modelchecking.cpp @@ -4,12 +4,24 @@ template using CheckTask = storm::modelchecker::CheckTask; -// Thin wrapper for model checking +// Thin wrapper for model checking using sparse engine template std::shared_ptr modelCheckingSparseEngine(std::shared_ptr> model, CheckTask const& task) { return storm::api::verifyWithSparseEngine(model, task); } +// Thin wrapper for model checking using dd engine +template +std::shared_ptr modelCheckingDdEngine(std::shared_ptr> model, CheckTask const& task) { + return storm::api::verifyWithDdEngine(model, task); +} + +// Thin wrapper for model checking using hybrid engine +template +std::shared_ptr modelCheckingHybridEngine(std::shared_ptr> model, CheckTask const& task) { + return storm::api::verifyWithHybridEngine(model, task); +} + // Thin wrapper for computing prob01 states template std::pair computeProb01(storm::models::sparse::Dtmc const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) { @@ -42,8 +54,12 @@ void define_modelchecking(py::module& m) { ; // Model checking - m.def("_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform model checking", py::arg("model"), py::arg("task")); - m.def("_parametric_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform parametric model checking", py::arg("model"), py::arg("task")); + m.def("_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform model checking using the sparse engine", py::arg("model"), py::arg("task")); + m.def("_parametric_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform parametric model checking using the sparse engine", py::arg("model"), py::arg("task")); + m.def("_model_checking_dd_engine", &modelCheckingDdEngine, "Perform model checking using the dd engine", py::arg("model"), py::arg("task")); + m.def("_parametric_model_checking_dd_engine", &modelCheckingDdEngine, "Perform parametric model checking using the dd engine", py::arg("model"), py::arg("task")); + m.def("_model_checking_hybrid_engine", &modelCheckingHybridEngine, "Perform model checking using the hybrid engine", py::arg("model"), py::arg("task")); + m.def("_parametric_model_checking_hybrid_engine", &modelCheckingHybridEngine, "Perform parametric model checking using the hybrid engine", py::arg("model"), py::arg("task")); m.def("_compute_prob01states_double", &computeProb01, "Compute prob-0-1 states", py::arg("model"), py::arg("phi_states"), py::arg("psi_states")); m.def("_compute_prob01states_rationalfunc", &computeProb01, "Compute prob-0-1 states", py::arg("model"), py::arg("phi_states"), py::arg("psi_states")); m.def("_compute_prob01states_min_double", &computeProb01min, "Compute prob-0-1 states (min)", py::arg("model"), py::arg("phi_states"), py::arg("psi_states")); diff --git a/src/core/result.cpp b/src/core/result.cpp index b564e3c..9025c63 100644 --- a/src/core/result.cpp +++ b/src/core/result.cpp @@ -1,11 +1,9 @@ #include "result.h" #include "storm/analysis/GraphConditions.h" - -// Thin wrapper -template -std::vector getValues(storm::modelchecker::ExplicitQuantitativeCheckResult const& result) { - return result.getValueVector(); -} +#include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" +#include "storm/modelchecker/results/SymbolicQuantitativeCheckResult.h" +#include "storm/modelchecker/results/HybridQuantitativeCheckResult.h" +#include "storm/models/symbolic/StandardRewardModel.h" // Define python bindings void define_result(py::module& m) { @@ -49,6 +47,8 @@ void define_result(py::module& m) { }, py::arg("state"), "Get result for given state") .def("get_truth_values", &storm::modelchecker::ExplicitQualitativeCheckResult::getTruthValuesVector, "Get BitVector representing the truth values") ; + py::class_, std::shared_ptr>>(m, "SymbolicQualitativeCheckResult", "Symbolic qualitative model checking result", qualitativeCheckResult) + ; // QuantitativeCheckResult py::class_, std::shared_ptr>> quantitativeCheckResult(m, "_QuantitativeCheckResult", "Abstract class for quantitative model checking results", checkResult); @@ -56,15 +56,27 @@ void define_result(py::module& m) { .def("at", [](storm::modelchecker::ExplicitQuantitativeCheckResult const& result, storm::storage::sparse::state_type state) { return result[state]; }, py::arg("state"), "Get result for given state") - .def("get_values", &getValues, "Get model checking result values for all states") + .def("get_values", [](storm::modelchecker::ExplicitQuantitativeCheckResult const& res) {return res.getValueVector();}, "Get model checking result values for all states") .def_property_readonly("scheduler", [](storm::modelchecker::ExplicitQuantitativeCheckResult const& res) {return res.getScheduler();}, "get scheduler") ; + py::class_, std::shared_ptr>>(m, "SymbolicQuantitativeCheckResult", "Symbolic quantitative model checking result", quantitativeCheckResult) + ; + py::class_, std::shared_ptr>>(m, "HybridQuantitativeCheckResult", "Hybrid quantitative model checking result", quantitativeCheckResult) + .def("get_values", &storm::modelchecker::HybridQuantitativeCheckResult::getExplicitValueVector, "Get model checking result values for all states") + ; + py::class_, std::shared_ptr>> parametricQuantitativeCheckResult(m, "_ParametricQuantitativeCheckResult", "Abstract class for parametric quantitative model checking results", checkResult); py::class_, std::shared_ptr>>(m, "ExplicitParametricQuantitativeCheckResult", "Explicit parametric quantitative model checking result", parametricQuantitativeCheckResult) .def("at", [](storm::modelchecker::ExplicitQuantitativeCheckResult const& result, storm::storage::sparse::state_type state) { return result[state]; }, py::arg("state"), "Get result for given state") - .def("get_values", &getValues, "Get model checking result values for all states") + .def("get_values", [](storm::modelchecker::ExplicitQuantitativeCheckResult const& res) { return res.getValueVector();}, "Get model checking result values for all states") + ; + py::class_, std::shared_ptr>>(m, "SymbolicParametricQuantitativeCheckResult", "Symbolic parametric quantitative model checking result", quantitativeCheckResult) ; + py::class_, std::shared_ptr>>(m, "HybridParametricQuantitativeCheckResult", "Symbolic parametric hybrid quantitative model checking result", quantitativeCheckResult) + .def("get_values", &storm::modelchecker::HybridQuantitativeCheckResult::getExplicitValueVector, "Get model checking result values for all states") + ; + } diff --git a/tests/core/test_modelchecking.py b/tests/core/test_modelchecking.py index 89cce9e..c8687bc 100644 --- a/tests/core/test_modelchecking.py +++ b/tests/core/test_modelchecking.py @@ -127,3 +127,24 @@ class TestModelChecking: assert initial_state == 1 result = stormpy.model_checking(model, formulas[0]) assert math.isclose(result.at(initial_state), 4.166666667) + + def test_model_checking_prism_dd_dtmc(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"one\" ]", program) + model = stormpy.build_symbolic_model(program, formulas) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + result = stormpy.check_model_dd(model, formulas[0]) + assert type(result) is stormpy.SymbolicQuantitativeCheckResult + + def test_model_checking_prism_hybrid_dtmc(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"one\" ]", program) + model = stormpy.build_symbolic_model(program, formulas) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + result = stormpy.check_model_hybrid(model, formulas[0]) + assert type(result) is stormpy.HybridQuantitativeCheckResult + values = result.get_values() + assert len(values) == 3 + assert math.isclose(values[0], 0.16666666666666663) diff --git a/tests/pars/test_parametric.py b/tests/pars/test_parametric.py index 71944e4..677a6ff 100644 --- a/tests/pars/test_parametric.py +++ b/tests/pars/test_parametric.py @@ -8,7 +8,7 @@ from configurations import pars @pars class TestParametric: - def test_parametric_state_elimination(self): + def test_parametric_model_checking_sparse(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) prop = "P=? [F s=5]" formulas = stormpy.parse_properties_for_prism_program(prop, program) @@ -24,6 +24,32 @@ class TestParametric: one = stormpy.FactorizedPolynomial(stormpy.RationalRF(1)) assert func.denominator == one + def test_parametric_model_checking_dd(self): + program = stormpy.parse_prism_program(get_example_path("pdtmc", "parametric_die.pm")) + prop = "P=? [F s=5]" + formulas = stormpy.parse_properties_for_prism_program(prop, program) + model = stormpy.build_symbolic_parametric_model(program, formulas) + assert model.nr_states == 11 + assert model.nr_transitions == 17 + assert model.model_type == stormpy.ModelType.DTMC + assert model.has_parameters + result = stormpy.check_model_dd(model, formulas[0]) + assert type(result) is stormpy.SymbolicParametricQuantitativeCheckResult + + def test_parametric_model_checking_hybrid(self): + program = stormpy.parse_prism_program(get_example_path("pdtmc", "parametric_die.pm")) + prop = "P=? [F s=5]" + formulas = stormpy.parse_properties_for_prism_program(prop, program) + model = stormpy.build_symbolic_parametric_model(program, formulas) + assert model.nr_states == 11 + assert model.nr_transitions == 17 + assert model.model_type == stormpy.ModelType.DTMC + assert model.has_parameters + result = stormpy.check_model_hybrid(model, formulas[0]) + assert type(result) is stormpy.HybridParametricQuantitativeCheckResult + values = result.get_values() + assert len(values) == 3 + def test_constraints_collector(self): from pycarl.formula import FormulaType, Relation if stormpy.info.storm_ratfunc_use_cln(): From c30d5a1433f04b1b19d43866d22537779b20c800 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 17:47:02 +0200 Subject: [PATCH 048/147] Symbolic bisimulation --- lib/stormpy/__init__.py | 26 ++++++++++++++++++++++++++ src/core/bisimulation.cpp | 8 ++++++++ tests/core/test_bisimulation.py | 17 ++++++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 1424892..7d76643 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -217,6 +217,17 @@ def perform_bisimulation(model, properties, bisimulation_type): :param bisimulation_type: Type of bisimulation (weak or strong). :return: Model after bisimulation. """ + return perform_sparse_bisimulation(model, properties, bisimulation_type) + + +def perform_sparse_bisimulation(model, properties, bisimulation_type): + """ + Perform bisimulation on model in sparse representation. + :param model: Model. + :param properties: Properties to preserve during bisimulation. + :param bisimulation_type: Type of bisimulation (weak or strong). + :return: Model after bisimulation. + """ formulae = [prop.raw_formula for prop in properties] if model.supports_parameters: return core._perform_parametric_bisimulation(model, formulae, bisimulation_type) @@ -224,6 +235,21 @@ def perform_bisimulation(model, properties, bisimulation_type): return core._perform_bisimulation(model, formulae, bisimulation_type) +def perform_symbolic_bisimulation(model, properties): + """ + Perform bisimulation on model in symbolic representation. + :param model: Model. + :param properties: Properties to preserve during bisimulation. + :return: Model after bisimulation. + """ + formulae = [prop.raw_formula for prop in properties] + bisimulation_type = BisimulationType.STRONG + if model.supports_parameters: + return core._perform_symbolic_parametric_bisimulation(model, formulae, bisimulation_type) + else: + return core._perform_symbolic_bisimulation(model, formulae, bisimulation_type) + + def model_checking(model, property, only_initial_states=False, extract_scheduler=False): """ Perform model checking on model for property. diff --git a/src/core/bisimulation.cpp b/src/core/bisimulation.cpp index 7feddd0..e7746ba 100644 --- a/src/core/bisimulation.cpp +++ b/src/core/bisimulation.cpp @@ -1,11 +1,19 @@ #include "bisimulation.h" + +template +std::shared_ptr> performBisimulationMinimization(std::shared_ptr> const& model, std::vector> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong) { + return storm::api::performBisimulationMinimization(model, formulas, bisimulationType, storm::dd::bisimulation::SignatureMode::Eager); +} + // Define python bindings void define_bisimulation(py::module& m) { // Bisimulation m.def("_perform_bisimulation", &storm::api::performBisimulationMinimization, "Perform bisimulation", py::arg("model"), py::arg("formulas"), py::arg("bisimulation_type")); m.def("_perform_parametric_bisimulation", &storm::api::performBisimulationMinimization, "Perform bisimulation on parametric model", py::arg("model"), py::arg("formulas"), py::arg("bisimulation_type")); + m.def("_perform_symbolic_bisimulation", &performBisimulationMinimization, "Perform bisimulation", py::arg("model"), py::arg("formulas"), py::arg("bisimulation_type")); + m.def("_perform_symbolic_parametric_bisimulation", &performBisimulationMinimization, "Perform bisimulation on parametric model", py::arg("model"), py::arg("formulas"), py::arg("bisimulation_type")); // BisimulationType py::enum_(m, "BisimulationType", "Types of bisimulation") diff --git a/tests/core/test_bisimulation.py b/tests/core/test_bisimulation.py index 418defc..b16ccc2 100644 --- a/tests/core/test_bisimulation.py +++ b/tests/core/test_bisimulation.py @@ -31,6 +31,21 @@ class TestBisimulation: assert initial_state_bisim == 34 assert math.isclose(result.at(initial_state), result_bisim.at(initial_state_bisim), rel_tol=1e-4) + def test_symbolic_bisimulation(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "crowds5_5.pm")) + prop = "P=? [F \"observe0Greater1\"]" + properties = stormpy.parse_properties_for_prism_program(prop, program) + model = stormpy.build_symbolic_model(program, properties) + assert model.nr_states == 7403 + assert model.nr_transitions == 13041 + assert model.model_type == stormpy.ModelType.DTMC + assert not model.supports_parameters + model_bisim = stormpy.perform_symbolic_bisimulation(model, properties) + assert model_bisim.nr_states == 65 + assert model_bisim.nr_transitions == 105 + assert model_bisim.model_type == stormpy.ModelType.DTMC + assert not model_bisim.supports_parameters + def test_parametric_bisimulation(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) assert program.nr_modules == 5 @@ -60,4 +75,4 @@ class TestBisimulation: initial_state_bisim = model_bisim.initial_states[0] assert initial_state_bisim == 316 ratFunc_bisim = result_bisim.at(initial_state_bisim) - assert ratFunc == ratFunc_bisim + assert ratFunc == ratFunc_bisim \ No newline at end of file From 054df185c0f49f96052217ea02582745dbc5ca7c Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 18 May 2018 18:33:29 +0200 Subject: [PATCH 049/147] Transformation from symbolic model to sparse model --- lib/stormpy/__init__.py | 11 ++++++++++ src/core/transformation.cpp | 7 +++++++ src/core/transformation.h | 5 +++++ src/mod_core.cpp | 2 ++ tests/core/test_transformation.py | 35 +++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+) create mode 100644 src/core/transformation.cpp create mode 100644 src/core/transformation.h create mode 100644 tests/core/test_transformation.py diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 7d76643..9d3829c 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -332,6 +332,17 @@ def check_model_hybrid(model, property, only_initial_states=False): task = core.CheckTask(formula, only_initial_states) return core._model_checking_hybrid_engine(model, task) +def transform_to_sparse_model(model): + """ + Transform model in symbolic representation into model in sparse representation. + :param model: Symbolic model. + :return: Sparse model. + """ + if model.supports_parameters: + return core._transform_to_sparse_parametric_model(model) + else: + return core._transform_to_sparse_model(model) + def prob01min_states(model, eventually_formula): assert type(eventually_formula) == logic.EventuallyFormula diff --git a/src/core/transformation.cpp b/src/core/transformation.cpp new file mode 100644 index 0000000..984c0e8 --- /dev/null +++ b/src/core/transformation.cpp @@ -0,0 +1,7 @@ +#include "transformation.h" + +void define_transformation(py::module& m) { + // Transform model + m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model")); + m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model")); +} \ No newline at end of file diff --git a/src/core/transformation.h b/src/core/transformation.h new file mode 100644 index 0000000..d098346 --- /dev/null +++ b/src/core/transformation.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +void define_transformation(py::module& m); diff --git a/src/mod_core.cpp b/src/mod_core.cpp index 615a7ef..5a84f58 100644 --- a/src/mod_core.cpp +++ b/src/mod_core.cpp @@ -7,6 +7,7 @@ #include "core/input.h" #include "core/analysis.h" #include "core/environment.h" +#include "core/transformation.h" PYBIND11_MODULE(core, m) { m.doc() = "core"; @@ -28,4 +29,5 @@ PYBIND11_MODULE(core, m) { define_input(m); define_graph_constraints(m); define_environment(m); + define_transformation(m); } diff --git a/tests/core/test_transformation.py b/tests/core/test_transformation.py new file mode 100644 index 0000000..562f85f --- /dev/null +++ b/tests/core/test_transformation.py @@ -0,0 +1,35 @@ +import stormpy +import stormpy.logic +from helpers.helper import get_example_path + + +class TestTransformation: + def test_transform_symbolic_dtmc_to_sparse(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "crowds5_5.pm")) + model = stormpy.build_symbolic_model(program) + assert model.nr_states == 8607 + assert model.nr_transitions == 15113 + assert model.model_type == stormpy.ModelType.DTMC + assert not model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanDtmc + symbolic_model = stormpy.transform_to_sparse_model(model) + assert symbolic_model.nr_states == 8607 + assert symbolic_model.nr_transitions == 15113 + assert symbolic_model.model_type == stormpy.ModelType.DTMC + assert not symbolic_model.supports_parameters + assert type(symbolic_model) is stormpy.SparseDtmc + + def test_transform_symbolic_parametric_dtmc_to_sparse(self): + program = stormpy.parse_prism_program(get_example_path("pdtmc", "parametric_die.pm")) + model = stormpy.build_symbolic_parametric_model(program) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + assert model.model_type == stormpy.ModelType.DTMC + assert model.supports_parameters + assert type(model) is stormpy.SymbolicSylvanParametricDtmc + symbolic_model = stormpy.transform_to_sparse_model(model) + assert symbolic_model.nr_states == 13 + assert symbolic_model.nr_transitions == 20 + assert symbolic_model.model_type == stormpy.ModelType.DTMC + assert symbolic_model.supports_parameters + assert type(symbolic_model) is stormpy.SparseParametricDtmc From c42ccaf644f057cb959c37593a877c12826d4a1f Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 22 May 2018 10:30:48 +0200 Subject: [PATCH 050/147] Added missing include --- src/core/bisimulation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/bisimulation.cpp b/src/core/bisimulation.cpp index e7746ba..6926791 100644 --- a/src/core/bisimulation.cpp +++ b/src/core/bisimulation.cpp @@ -1,4 +1,5 @@ #include "bisimulation.h" +#include "storm/models/symbolic/StandardRewardModel.h" template From 78a56cf73204123eac5e75a546ad856bba9dd537 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 22 May 2018 11:04:39 +0200 Subject: [PATCH 051/147] Added missing include --- src/core/modelchecking.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/modelchecking.cpp b/src/core/modelchecking.cpp index 1084a12..7faafe0 100644 --- a/src/core/modelchecking.cpp +++ b/src/core/modelchecking.cpp @@ -1,5 +1,6 @@ #include "modelchecking.h" #include "result.h" +#include "storm/models/symbolic/StandardRewardModel.h" template using CheckTask = storm::modelchecker::CheckTask; From 5d4c344a9c58f0ccbcd61809900dd35c63afdfd7 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 22 May 2018 12:55:59 +0200 Subject: [PATCH 052/147] Added missing include --- src/core/transformation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/transformation.cpp b/src/core/transformation.cpp index 984c0e8..6db4330 100644 --- a/src/core/transformation.cpp +++ b/src/core/transformation.cpp @@ -1,4 +1,5 @@ #include "transformation.h" +#include "storm/models/symbolic/StandardRewardModel.h" void define_transformation(py::module& m) { // Transform model From 3cced14e294a8b2d503059b2f75c0d0bb90b9ee9 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 22 May 2018 14:48:28 +0200 Subject: [PATCH 053/147] Typo --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c6542a8..deedadd 100755 --- a/setup.py +++ b/setup.py @@ -190,7 +190,7 @@ class CMakeBuild(build_ext): env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), self.distribution.get_version()) setup_helper.ensure_dir_exists(self.build_temp) - print("Pycarl - CMake args={}".format(cmake_args)) + print("Stormpy - CMake args={}".format(cmake_args)) # Call cmake subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) subprocess.check_call(['cmake', '--build', '.', '--target', ext.name] + build_args, cwd=self.build_temp) From 5cdc14bb0aee2cae9ecb419e6f7aeffee3e4face Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 29 May 2018 20:05:58 +0200 Subject: [PATCH 054/147] Use find_library to search for storm libs --- CMakeLists.txt | 1 + cmake/CMakeLists.txt | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0d0130..af8c593 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ option(STORMPY_DISABLE_SIGNATURE_DOC "disables the signature in the documentatio configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/generated/config.h) message(STATUS "STORM-DIR: ${storm_DIR}") +message(STATUS "STORM-INCLUDE-DIR: ${storm_INCLUDE_DIR}") function(stormpy_module NAME) # second, optional argument are ADDITIONAL_LIBRARIES diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index bbaf9c4..62123d9 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -6,20 +6,19 @@ find_package(storm REQUIRED) # Set configuration set(STORM_DIR ${storm_DIR}) set(STORM_VERSION ${storm_VERSION}) +set(STORM_LIBS ${storm_LIBRARIES}) # Check for storm-pars -if(EXISTS "${storm_DIR}/lib/libstorm-pars.dylib") - set(HAVE_STORM_PARS TRUE) -elseif(EXISTS "${storm_DIR}/lib/libstorm-pars.so") +find_library(STORM_PARS NAMES storm-pars HINTS "${storm_DIR}/lib/") +if(STORM_PARS) set(HAVE_STORM_PARS TRUE) else() set(HAVE_STORM_PARS FALSE) endif() # Check for storm-dft -if(EXISTS "${storm_DIR}/lib/libstorm-dft.dylib") - set(HAVE_STORM_DFT TRUE) -elseif(EXISTS "${storm_DIR}/lib/libstorm-dft.so") +find_library(STORM_DFT NAMES storm-dft HINTS "${storm_DIR}/lib/") +if(STORM_DFT) set(HAVE_STORM_DFT TRUE) else() set(HAVE_STORM_DFT FALSE) From 5f4bcfa61c9a3a67db45eb00f60461ef501c153b Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 8 Jun 2018 13:15:50 +0200 Subject: [PATCH 055/147] switches for debug output --- src/core/core.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/core.cpp b/src/core/core.cpp index dddb7d6..676f1aa 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -11,6 +11,15 @@ void define_core(py::module& m) { storm::settings::initializeAll("StoRM-Py", "stormpy"); storm::settings::SettingsManager::manager().setFromString(args); }, "Initialize Storm", py::arg("arguments")); + + m.def("_set_loglevel_debug", []() { + storm::utility::setLogLevel(l3pp::LogLevel::DEBUG); + }, "set loglevel for storm to debug"); + m.def("_set_loglevel_trace", []() { + storm::utility::setLogLevel(l3pp::LogLevel::TRACE); + }); + + } void define_parse(py::module& m) { From 668753696a710c1c1216d10c7a7a03d73a22fee6 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 8 Jun 2018 13:17:09 +0200 Subject: [PATCH 056/147] more building options --- src/core/core.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 676f1aa..104f020 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -90,7 +90,9 @@ void define_build(py::module& m) { py::class_(m, "BuilderOptions", "Options for building process") .def(py::init> const&>(), "Initialise with formulae to preserve", py::arg("formulae")) .def(py::init(), "Initialise without formulae", py::arg("build_all_reward_models"), py::arg("build_all_labels")) - .def("set_build_with_choice_origins", &storm::builder::BuilderOptions::setBuildChoiceOrigins, "Build choice origins", py::arg("new_value")); + .def("set_build_with_choice_origins", &storm::builder::BuilderOptions::setBuildChoiceOrigins, "Build choice origins", py::arg("new_value")=true) + .def("set_add_out_of_bounds_state", &storm::builder::BuilderOptions::setAddOutOfBoundsState, "Build with out of bounds state", py::arg("new_value")=true) + .def("set_add_overlapping_guards_label", &storm::builder::BuilderOptions::setAddOverlappingGuardsLabel, "Build with overlapping guards state labeled", py::arg("new_value")=true); } void define_optimality_type(py::module& m) { From 2ac815b8269378d400ae108b2ed458d6b9e0715a Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 8 Jun 2018 13:17:56 +0200 Subject: [PATCH 057/147] extended wrapper for counterexamples: --- src/core/counterexample.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index 6fbb6a1..7f8be55 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -5,11 +5,22 @@ using namespace storm::counterexamples; // Define python bindings void define_counterexamples(py::module& m) { - py::class_>(m, "FlatSet", "Container to pass to program"); - - + py::class_>(m, "FlatSet", "Container to pass to program") + .def(py::init<>()) + .def("insert", [](boost::container::flat_set& flatset, uint64_t value) {flatset.insert(value);}) + .def("__str__", [](boost::container::flat_set const& set) { std::stringstream str; str << "["; for(auto const& i : set) { str << i << ", ";} str << "]"; return str.str(); }) + .def("__len__", [](boost::container::flat_set const& set) { return set.size();}) + ; + using CexGeneratorOptions = SMTMinimalLabelSetGenerator::Options; + py::class_(m, "SMTCounterExampleGeneratorOptions", "Options for highlevel counterexample generation") + .def(py::init<>()) + .def_readwrite("check_threshold_feasible", &CexGeneratorOptions::checkThresholdFeasible) + .def_readwrite("encode_reachability", &CexGeneratorOptions::encodeReachability) + .def_readwrite("silent", &CexGeneratorOptions::silent) + .def_readwrite("use_dynamic_constraints", &CexGeneratorOptions::useDynamicConstraints) + ; py::class_>(m, "SMTCounterExampleGenerator", "Highlevel Counterexample Generator with SMT as backend"). - def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleLabelSet) + def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleLabelSet, "Compute counterexample", py::arg("env"), py::arg("symbolic_model"), py::arg("model"), py::arg("formula"), py::arg("dontcare"), py::arg("options")) ; From b649847363dd730a8cad200951293645e5060918 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 8 Jun 2018 13:18:55 +0200 Subject: [PATCH 058/147] extended access to prism/jani/expressions/formulae --- src/logic/formulae.cpp | 3 +++ src/storage/expressions.cpp | 3 ++- src/storage/jani.cpp | 7 +++++++ src/storage/prism.cpp | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index 5b73050..9294c00 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -1,6 +1,8 @@ #include "formulae.h" #include "storm/logic/Formulas.h" #include "storm/logic/CloneVisitor.h" +#include "storm/logic/LabelSubstitutionVisitor.h" + void define_formulae(py::module& m) { @@ -15,6 +17,7 @@ void define_formulae(py::module& m) { py::class_> formula(m, "Formula", "Generic Storm Formula"); formula.def("__str__", &storm::logic::Formula::toString) .def("clone", [](storm::logic::Formula const& f) { storm::logic::CloneVisitor cv; return cv.clone(f);}) + .def("substitute_labels_by_labels", [](storm::logic::Formula const& f, std::map const& labelSubs) {storm::logic::LabelSubstitutionVisitor lsv(labelSubs); return lsv.substitute(f);}, "substitute label occurences", py::arg("replacements")) ; // Path Formulae diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index 00799ec..f3f78b4 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -40,7 +40,8 @@ void define_expressions(py::module& m) { py::class_>(m, "Expression", "Holds an expression") .def("contains_variables", &storm::expressions::Expression::containsVariables, "Check if the expression contains variables.") .def("contains_variable", &storm::expressions::Expression::containsVariable, "Check if the expression contains any of the given variables.", py::arg("variables")) - .def("is_literal", &storm::expressions::Expression::isLiteral, "Check if the expression is a literal") + .def("get_variables" , &storm::expressions::Expression::getVariables, "Get the variables") + .def("is_literal", &storm::expressions::Expression::isLiteral, "Check if the expression is a literal") .def("has_boolean_type", &storm::expressions::Expression::hasBooleanType, "Check if the expression is a boolean") .def("has_integer_type", &storm::expressions::Expression::hasIntegerType, "Check if the expression is an integer") .def("has_rational_type", &storm::expressions::Expression::hasRationalType, "Check if the expression is a rational") diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index 3574a35..192f680 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -1,6 +1,7 @@ #include "jani.h" #include #include +#include #include "src/helpers.h" using namespace storm::jani; @@ -14,10 +15,16 @@ std::string janiToString(Model const& m) { void define_jani(py::module& m) { py::class_> md(m, "JaniModel", "A Jani Model"); md.def_property_readonly("name", &Model::getName, "model name") + .def_property_readonly("model_type", &storm::jani::Model::getModelType, "Model type") .def_property_readonly("automata", [](const Model& model) {return model.getAutomata();}, "get automata") .def_property_readonly("constants", [](const Model& model) {return model.getConstants();}, "get constants") .def("restrict_edges", &Model::restrictEdges, "restrict model to edges given by set", py::arg("edge_set")) + .def_property_readonly("expression_manager", &Model::getExpressionManager, "get expression manager", pybind11::return_value_policy::reference_internal) + .def_property_readonly("has_undefined_constants", &Model::hasUndefinedConstants, "Flag if program has undefined constants") + .def_property_readonly("undefined_constants_are_graph_preserving", &Model::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") .def("__str__", &janiToString) + .def("define_constants", &Model::defineUndefinedConstants, "define constants with a mapping from the corresponding expression variables to expressions", py::arg("map")) + .def("substitute_constants", &Model::substituteConstants, "substitute constants") .def("get_automaton_index", &Model::getAutomatonIndex, "get index for automaton name") .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index 01338ea..1f52288 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -20,7 +20,7 @@ void define_prism(py::module& m) { .def("simplify", &Program::simplify, "Simplify") .def("used_constants",&Program::usedConstants, "Compute Used Constants") .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") - .def("to_jani", &Program::toJani, "Transform to Jani program", py::arg("all_variables_global")=false) + .def("to_jani", &Program::toJaniWithLabelRenaming, "Transform to Jani program", py::arg("all_variables_global")=false, py::arg("suffix") = "") .def("__str__", &streamToString); py::class_ module(m, "PrismModule", "A module in a Prism program"); @@ -57,7 +57,7 @@ void define_prism(py::module& m) { constant.def_property_readonly("name", &Constant::getName, "Constant name") .def_property_readonly("defined", &Constant::isDefined, "Is the constant defined?") .def_property_readonly("type", &Constant::getType, "The type of the constant") - .def_property_readonly("variable", &Constant::getExpressionVariable, "Expression variable") + .def_property_readonly("expression_variable", &Constant::getExpressionVariable, "Expression variable") ; From 70a54e26c9e0e3e3f9b73a47845261149cb64950 Mon Sep 17 00:00:00 2001 From: sjunges Date: Sun, 10 Jun 2018 21:29:45 +0200 Subject: [PATCH 059/147] updated based on changes in storm --- CMakeLists.txt | 8 ++++---- src/core/core.cpp | 1 + src/core/counterexample.cpp | 1 + src/core/input.cpp | 1 + src/core/modelchecking.cpp | 1 + src/core/result.cpp | 3 +++ src/pars/common.h | 3 +++ src/storage/expressions.cpp | 2 +- 8 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index af8c593..cbe6851 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,11 +20,11 @@ function(stormpy_module NAME) if(ARGC GREATER 1) # Additional libraries - target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${ARGV2} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) - target_link_libraries(${NAME} PRIVATE storm ${ARGV1}) + target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${storm-parsers_INCLUDE_DIR} ${storm-counterexamples_INCLUDE_DIR} ${ARGV2} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) + target_link_libraries(${NAME} PRIVATE storm storm-parsers storm-counterexamples ${ARGV1}) else() - target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) - target_link_libraries(${NAME} PRIVATE storm) + target_include_directories(${NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${storm_INCLUDE_DIR} ${storm-parsers_INCLUDE_DIR} ${storm-counterexamples_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src/generated) + target_link_libraries(${NAME} PRIVATE storm storm-parsers storm-counterexamples) endif() endfunction(stormpy_module) diff --git a/src/core/core.cpp b/src/core/core.cpp index 8df3e2e..bbb8790 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -5,6 +5,7 @@ #include "storm/storage/dd/DdType.h" #include "storm/solver/OptimizationDirection.h" #include "storm/models/symbolic/StandardRewardModel.h" +#include "storm-parsers/api/storm-parsers.h" void define_core(py::module& m) { diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index 7f8be55..011313e 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -1,5 +1,6 @@ #include "counterexample.h" #include "storm/environment/Environment.h" +#include "storm-counterexamples/api/counterexamples.h" using namespace storm::counterexamples; diff --git a/src/core/input.cpp b/src/core/input.cpp index 29c7c3e..baf51dc 100644 --- a/src/core/input.cpp +++ b/src/core/input.cpp @@ -1,5 +1,6 @@ #include "input.h" #include "src/helpers.h" +#include "storm-parsers/api/storm-parsers.h" void define_property(py::module& m) { py::class_(m, "Property", "Property") diff --git a/src/core/modelchecking.cpp b/src/core/modelchecking.cpp index 7faafe0..862d4c3 100644 --- a/src/core/modelchecking.cpp +++ b/src/core/modelchecking.cpp @@ -1,6 +1,7 @@ #include "modelchecking.h" #include "result.h" #include "storm/models/symbolic/StandardRewardModel.h" +#include "storm/modelchecker/results/CheckResult.h" template using CheckTask = storm::modelchecker::CheckTask; diff --git a/src/core/result.cpp b/src/core/result.cpp index 45ac4a7..4e6728d 100644 --- a/src/core/result.cpp +++ b/src/core/result.cpp @@ -3,6 +3,9 @@ #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "storm/modelchecker/results/SymbolicQuantitativeCheckResult.h" #include "storm/modelchecker/results/HybridQuantitativeCheckResult.h" +#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" + #include "storm/models/symbolic/StandardRewardModel.h" // Define python bindings diff --git a/src/pars/common.h b/src/pars/common.h index 79a9840..e7a604d 100644 --- a/src/pars/common.h +++ b/src/pars/common.h @@ -1,3 +1,6 @@ #include "src/common.h" #include "storm-pars/api/storm-pars.h" + +#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" \ No newline at end of file diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index f3f78b4..4132c1e 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -3,7 +3,7 @@ #include "storm/storage/expressions/ExpressionManager.h" #include "storm/storage/expressions/Expression.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" //Define python bindings void define_expressions(py::module& m) { From 2cd54a1e0d3d943a49ed8cf814197e4fef2a3d3a Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 21 Jun 2018 10:38:25 +0200 Subject: [PATCH 060/147] Updated CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f4233..d5e60a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ Version 1.2.x ### Version 1.2.x Requires storm version >= 1.2.2 and pycarl version >= 2.0.2 +- Adaptions to changes in Storm +- Bindings for symbolic models: + * building symbolic models + * bisimulation + * transforming symbolic to sparse models +- Extraction of schedulers and queries on schedulers +- Extended PLA bindings +- Extended documentation ### Version 1.2.0 Requires storm version >= 1.2.0 and pycarl version >= 2.0.2 From 367fa419dd9f8965f6c9623add16336dd3f806a4 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 28 Jun 2018 13:01:12 +0200 Subject: [PATCH 061/147] add some fields to counterexample options --- src/core/counterexample.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index 011313e..b7a5f7e 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -19,6 +19,9 @@ void define_counterexamples(py::module& m) { .def_readwrite("encode_reachability", &CexGeneratorOptions::encodeReachability) .def_readwrite("silent", &CexGeneratorOptions::silent) .def_readwrite("use_dynamic_constraints", &CexGeneratorOptions::useDynamicConstraints) + .def_readwrite("maximum_counterexamples", &CexGeneratorOptions::maximumCounterexamples) + .def_readwrite("continue_after_first_counterexample", &CexGeneratorOptions::continueAfterFirstCounterexampleUntil) + ; py::class_>(m, "SMTCounterExampleGenerator", "Highlevel Counterexample Generator with SMT as backend"). def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleLabelSet, "Compute counterexample", py::arg("env"), py::arg("symbolic_model"), py::arg("model"), py::arg("formula"), py::arg("dontcare"), py::arg("options")) From c0ac30fcd2d3eed5c59f3e954d567dbbbcf2f783 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 15:41:17 +0200 Subject: [PATCH 062/147] access to: preserved label names during building --- src/core/core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/core.cpp b/src/core/core.cpp index bbb8790..b592420 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -106,6 +106,7 @@ void define_build(py::module& m) { py::class_(m, "BuilderOptions", "Options for building process") .def(py::init> const&>(), "Initialise with formulae to preserve", py::arg("formulae")) .def(py::init(), "Initialise without formulae", py::arg("build_all_reward_models"), py::arg("build_all_labels")) + .def_property_readonly("preserved_label_names", &storm::builder::BuilderOptions::getLabelNames, "Labels preserved") .def("set_build_with_choice_origins", &storm::builder::BuilderOptions::setBuildChoiceOrigins, "Build choice origins", py::arg("new_value")=true) .def("set_add_out_of_bounds_state", &storm::builder::BuilderOptions::setAddOutOfBoundsState, "Build with out of bounds state", py::arg("new_value")=true) .def("set_add_overlapping_guards_label", &storm::builder::BuilderOptions::setAddOverlappingGuardsLabel, "Build with overlapping guards state labeled", py::arg("new_value")=true); From db71b7e5fd6e6d0b3768255b2486128bcf41dde7 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 15:41:43 +0200 Subject: [PATCH 063/147] fixed transformation after changes in storm --- src/core/transformation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/transformation.cpp b/src/core/transformation.cpp index 6db4330..12507d8 100644 --- a/src/core/transformation.cpp +++ b/src/core/transformation.cpp @@ -3,6 +3,6 @@ void define_transformation(py::module& m) { // Transform model - m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model")); - m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model")); + m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model"), py::arg("formulae")); + m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model"), py::arg("formulae")); } \ No newline at end of file From f08df44acf542ad71fa1c294e6cd700ab417dbc4 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 15:42:31 +0200 Subject: [PATCH 064/147] access to is prop operator, is reward operator in formulae --- src/logic/formulae.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index 9294c00..8b80dea 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -18,7 +18,10 @@ void define_formulae(py::module& m) { formula.def("__str__", &storm::logic::Formula::toString) .def("clone", [](storm::logic::Formula const& f) { storm::logic::CloneVisitor cv; return cv.clone(f);}) .def("substitute_labels_by_labels", [](storm::logic::Formula const& f, std::map const& labelSubs) {storm::logic::LabelSubstitutionVisitor lsv(labelSubs); return lsv.substitute(f);}, "substitute label occurences", py::arg("replacements")) - ; + .def_property_readonly("is_probability_operator", &storm::logic::Formula::isProbabilityOperatorFormula, "is it a probability operator") + .def_property_readonly("is_reward_operator", &storm::logic::Formula::isRewardOperatorFormula, "is it a reward operator") + + ; // Path Formulae py::class_> pathFormula(m, "PathFormula", "Formula about the probability of a set of paths in an automaton", formula); From 4ef4844c304dcf0dff71e247494c97b645b82889 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 15:43:06 +0200 Subject: [PATCH 065/147] std::chrono support in stormpy --- src/mod_utility.cpp | 2 ++ src/utility/chrono.cpp | 8 ++++++++ src/utility/chrono.h | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 src/utility/chrono.cpp create mode 100644 src/utility/chrono.h diff --git a/src/mod_utility.cpp b/src/mod_utility.cpp index 8d1a504..777e80a 100644 --- a/src/mod_utility.cpp +++ b/src/mod_utility.cpp @@ -2,6 +2,7 @@ #include "utility/shortestPaths.h" #include "utility/smtsolver.h" +#include "utility/chrono.h" PYBIND11_MODULE(utility, m) { @@ -9,4 +10,5 @@ PYBIND11_MODULE(utility, m) { define_ksp(m); define_smt(m); + define_chrono(m); } diff --git a/src/utility/chrono.cpp b/src/utility/chrono.cpp new file mode 100644 index 0000000..def183e --- /dev/null +++ b/src/utility/chrono.cpp @@ -0,0 +1,8 @@ +#include "chrono.h" +#include "src/helpers.h" + +void define_chrono(py::module& m) { + py::class_(m, "milliseconds") + .def("count", &std::chrono::milliseconds::count) + .def("__str__", [](std::chrono::milliseconds const& t) { std::stringstream strstr; strstr << t.count(); return strstr.str(); }); +} \ No newline at end of file diff --git a/src/utility/chrono.h b/src/utility/chrono.h new file mode 100644 index 0000000..42762f5 --- /dev/null +++ b/src/utility/chrono.h @@ -0,0 +1,5 @@ +#pragma once + +#include "src/common.h" + +void define_chrono(py::module& m); \ No newline at end of file From 7bb7023e018ff57de9626852ead00d3ddf2cb109 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 15:58:09 +0200 Subject: [PATCH 066/147] transformation: default no formulae given --- src/core/transformation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/transformation.cpp b/src/core/transformation.cpp index 12507d8..bdf939c 100644 --- a/src/core/transformation.cpp +++ b/src/core/transformation.cpp @@ -3,6 +3,6 @@ void define_transformation(py::module& m) { // Transform model - m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model"), py::arg("formulae")); - m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model"), py::arg("formulae")); + m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model"), py::arg("formulae") = std::vector>()); + m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model"), py::arg("formulae") = std::vector>()); } \ No newline at end of file From 38b4960d8f4fa0f3620ebb2d4fc956eea0f082fd Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 16:25:06 +0200 Subject: [PATCH 067/147] add precompute bindings --- src/core/counterexample.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index b7a5f7e..cd350db 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -24,8 +24,16 @@ void define_counterexamples(py::module& m) { ; py::class_>(m, "SMTCounterExampleGenerator", "Highlevel Counterexample Generator with SMT as backend"). - def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleLabelSet, "Compute counterexample", py::arg("env"), py::arg("symbolic_model"), py::arg("model"), py::arg("formula"), py::arg("dontcare"), py::arg("options")) + def_static("precompute", &SMTMinimalLabelSetGenerator::precompute, "Precompute input for counterexample generation", py::arg("env"), py::arg("symbolic_model"), py::arg("model"), py::arg("formula")). + def_static("build", &SMTMinimalLabelSetGenerator::computeCounterexampleLabelSet, "Compute counterexample", py::arg("env"), py::arg("stats"), py::arg("symbolic_model"), py::arg("model"), py::arg("cex_input"), py::arg("dontcare"), py::arg("options")) + + ; + using CexInput = SMTMinimalLabelSetGenerator::CexInput; + py::class_(m, "SMTCounterExampleInput", "Precomputed input for counterexample generation") + .def("add_reward_and_threshold", &CexInput::addRewardThresholdCombination, "add another reward structure and threshold", py::arg("reward_name"), py::arg("threshold")); + + } From 2bb610885a815e6b7c70a6129090d9c4d2498ad7 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 16:28:52 +0200 Subject: [PATCH 068/147] initialise counterexample settings --- src/core/core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index b592420..7b05c3b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -6,13 +6,14 @@ #include "storm/solver/OptimizationDirection.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm-parsers/api/storm-parsers.h" - +#include "storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.h" void define_core(py::module& m) { // Init m.def("_set_up", [](std::string const& args) { storm::utility::setUp(); storm::settings::initializeAll("StoRM-Py", "stormpy"); + storm::settings::addModule(); storm::settings::SettingsManager::manager().setFromString(args); }, "Initialize Storm", py::arg("arguments")); From 50f7e054f73d5a59bb80d3c075387e81098066a6 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 16:30:08 +0200 Subject: [PATCH 069/147] bindings for cex generator stats --- src/core/counterexample.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index cd350db..b572f5a 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -12,6 +12,19 @@ void define_counterexamples(py::module& m) { .def("__str__", [](boost::container::flat_set const& set) { std::stringstream str; str << "["; for(auto const& i : set) { str << i << ", ";} str << "]"; return str.str(); }) .def("__len__", [](boost::container::flat_set const& set) { return set.size();}) ; + + using CexGeneratorStats = SMTMinimalLabelSetGenerator::GeneratorStats; + + py::class_(m, "SMTCounterExampleGeneratorStats", "Stats for highlevel counterexample generation") + .def(py::init<>()) + .def_readonly("analysis_time", &CexGeneratorStats::analysisTime) + .def_readonly("setup_time", &CexGeneratorStats::setupTime) + .def_readonly("model_checking_time", &CexGeneratorStats::modelCheckingTime) + .def_readonly("solver_time", &CexGeneratorStats::solverTime); + + + + using CexGeneratorOptions = SMTMinimalLabelSetGenerator::Options; py::class_(m, "SMTCounterExampleGeneratorOptions", "Options for highlevel counterexample generation") .def(py::init<>()) From 5a16a600388b083de1637fae9badd7e2d624737e Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 18 Jul 2018 16:42:23 +0200 Subject: [PATCH 070/147] fixed spacing --- src/core/counterexample.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index b572f5a..e92445c 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -2,10 +2,13 @@ #include "storm/environment/Environment.h" #include "storm-counterexamples/api/counterexamples.h" + using namespace storm::counterexamples; // Define python bindings void define_counterexamples(py::module& m) { + + py::class_>(m, "FlatSet", "Container to pass to program") .def(py::init<>()) .def("insert", [](boost::container::flat_set& flatset, uint64_t value) {flatset.insert(value);}) @@ -22,9 +25,6 @@ void define_counterexamples(py::module& m) { .def_readonly("model_checking_time", &CexGeneratorStats::modelCheckingTime) .def_readonly("solver_time", &CexGeneratorStats::solverTime); - - - using CexGeneratorOptions = SMTMinimalLabelSetGenerator::Options; py::class_(m, "SMTCounterExampleGeneratorOptions", "Options for highlevel counterexample generation") .def(py::init<>()) From 9b1c64860a6d2532873452f172d769574177bd91 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 19 Jul 2018 15:42:27 +0200 Subject: [PATCH 071/147] Increase required storm version --- .travis.yml | 8 ++++---- setup.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5c081d..91d6a1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,13 +40,13 @@ jobs: # Docker Storm stable - os: linux compiler: gcc - env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 + env: TASK=Test CONFIG=Release DOCKER=storm:1.2.3 PYTHON=python3 script: travis/build.sh # Docker Storm stable in debug mode - os: linux compiler: gcc - env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 + env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.3-debug PYTHON=python3 script: travis/build.sh # Documentation @@ -68,6 +68,6 @@ jobs: # Allow failures of stable versions as new features might have been added allow_failures: - os: linux - env: TASK=Test CONFIG=Release DOCKER=storm:1.2.1 PYTHON=python3 + env: TASK=Test CONFIG=Release DOCKER=storm:1.2.3 PYTHON=python3 - os: linux - env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.1-debug PYTHON=python3 + env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.3-debug PYTHON=python3 diff --git a/setup.py b/setup.py index deedadd..9475487 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ if sys.version_info[0] == 2: sys.exit('Sorry, Python 2.x is not supported') # Minimal storm version required -storm_min_version = "1.2.2" +storm_min_version = "1.2.4" # Get the long description from the README file with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md'), encoding='utf-8') as f: From ae8615533bcd1c9d1969deca389ebd38a78eec1b Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 19 Jul 2018 16:18:47 +0200 Subject: [PATCH 072/147] Relaxed relative tolerance for some model checking results --- tests/core/test_modelchecking.py | 17 ++++++++--------- tests/pars/test_pla.py | 17 +++++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/tests/core/test_modelchecking.py b/tests/core/test_modelchecking.py index c8687bc..9e82b9f 100644 --- a/tests/core/test_modelchecking.py +++ b/tests/core/test_modelchecking.py @@ -16,7 +16,7 @@ class TestModelChecking: initial_state = model.initial_states[0] assert initial_state == 0 result = stormpy.model_checking(model, formulas[0]) - assert math.isclose(result.at(initial_state), 0.16666666666666663) + assert math.isclose(result.at(initial_state), 1 / 6) def test_model_checking_prism_mdp(self): program = stormpy.parse_prism_program(get_example_path("mdp", "coin2-2.nm")) @@ -28,8 +28,7 @@ class TestModelChecking: initial_state = model.initial_states[0] assert initial_state == 0 result = stormpy.model_checking(model, formulas[0]) - assert math.isclose(result.at(initial_state), 0.3828117384) - + assert math.isclose(result.at(initial_state), 49 / 128, rel_tol=1e-5) def test_model_checking_jani_dtmc(self): jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "die.jani")) @@ -41,7 +40,7 @@ class TestModelChecking: initial_state = model.initial_states[0] assert initial_state == 0 result = stormpy.model_checking(model, formula) - assert math.isclose(result.at(initial_state), 0.16666666666666663) + assert math.isclose(result.at(initial_state), 1 / 6) def test_model_checking_jani_dtmc(self): jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "die.jani")) @@ -66,10 +65,10 @@ class TestModelChecking: initial_state = model.initial_states[0] assert initial_state == 0 result = stormpy.model_checking(model, formulas[0]) - assert math.isclose(result.at(initial_state), 0.16666666666666663) + assert math.isclose(result.at(initial_state), 1 / 6) formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"two\" ]", program) result = stormpy.model_checking(model, formulas[0]) - assert math.isclose(result.at(initial_state), 0.16666666666666663) + assert math.isclose(result.at(initial_state), 1 / 6) def test_model_checking_all_dtmc(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) @@ -79,7 +78,7 @@ class TestModelChecking: assert model.nr_transitions == 20 result = stormpy.model_checking(model, formulas[0]) assert result.result_for_all_states - reference = [0.16666666666666663, 0.3333333333333333, 0, 0.6666666666666666, 0, 0, 0, 1, 0, 0, 0, 0, 0] + reference = [1 / 6, 1 / 3, 0, 2 / 3, 0, 0, 0, 1, 0, 0, 0, 0, 0] assert all(map(math.isclose, result.get_values(), reference)) def test_model_checking_only_initial(self): @@ -91,7 +90,7 @@ class TestModelChecking: assert initial_state == 0 result = stormpy.model_checking(model, formulas[0], only_initial_states=True) assert not result.result_for_all_states - assert math.isclose(result.at(initial_state), 0.125) + assert math.isclose(result.at(initial_state), 1 / 8) def test_model_checking_prob01(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) @@ -147,4 +146,4 @@ class TestModelChecking: assert type(result) is stormpy.HybridQuantitativeCheckResult values = result.get_values() assert len(values) == 3 - assert math.isclose(values[0], 0.16666666666666663) + assert math.isclose(values[0], 1 / 6) diff --git a/tests/pars/test_pla.py b/tests/pars/test_pla.py index 87a55d8..80a585f 100644 --- a/tests/pars/test_pla.py +++ b/tests/pars/test_pla.py @@ -44,10 +44,10 @@ class TestPLA: assert len(parameters) == 2 region = stormpy.pars.ParameterRegion("0.7<=pL<=0.9,0.75<=pK<=0.95", parameters) result = checker.get_bound(env, region, True) - assert math.isclose(float(result.constant_part()), 0.8369631383670559) - #result_vec = checker.get_bound_all_states(env, region, True) - #result = result_vec.at(model.initial_states[0]) - #assert math.isclose(float(result.constant_part()), 0.8369631383670559) + assert math.isclose(float(result.constant_part()), 0.8369631383670559, rel_tol=1e-6) + result_vec = checker.get_bound_all_states(env, region, True) + result = result_vec.at(model.initial_states[0]) + assert math.isclose(result, 0.8369631383670559, rel_tol=1e-6) def test_pla_manual(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) @@ -62,7 +62,7 @@ class TestPLA: assert len(parameters) == 2 region = stormpy.pars.ParameterRegion("0.7<=pL<=0.9,0.75<=pK<=0.95", parameters) result = checker.get_bound(env, region, True) - assert math.isclose(float(result.constant_part()), 0.8369631383670559) + assert math.isclose(float(result.constant_part()), 0.8369631383670559, rel_tol=1e-6) def test_pla_manual_no_simplification(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) @@ -77,7 +77,7 @@ class TestPLA: assert len(parameters) == 2 region = stormpy.pars.ParameterRegion("0.7<=pL<=0.9,0.75<=pK<=0.95", parameters) result = checker.get_bound(env, region, True) - assert math.isclose(float(result.constant_part()), 0.836963056082918) + assert math.isclose(float(result.constant_part()), 0.836963056082918, rel_tol=1e-6) def test_pla_state_bounds(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) @@ -93,7 +93,4 @@ class TestPLA: region = stormpy.pars.ParameterRegion("0.7<=pL<=0.9,0.75<=pK<=0.95", parameters) result_vec = checker.get_bound_all_states(env, region, True) assert len(result_vec.get_values()) == model.nr_states - assert math.isclose(result_vec.at(model.initial_states[0]), 0.836963056082918) - - - + assert math.isclose(result_vec.at(model.initial_states[0]), 0.836963056082918, rel_tol=1e-6) From d70f727c0b9beef8a6d35da36e8aaa2d44015023 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 19 Jul 2018 16:58:14 +0200 Subject: [PATCH 073/147] Updated BRP Jani file --- lib/stormpy/examples/files/dtmc/brp.jani | 2764 +++++++++------------- 1 file changed, 1066 insertions(+), 1698 deletions(-) diff --git a/lib/stormpy/examples/files/dtmc/brp.jani b/lib/stormpy/examples/files/dtmc/brp.jani index 8420dc2..22847fd 100644 --- a/lib/stormpy/examples/files/dtmc/brp.jani +++ b/lib/stormpy/examples/files/dtmc/brp.jani @@ -1,2033 +1,1401 @@ - { - "jani-version":1, - "name":"brp", - "type":"dtmc", - "features":[ - "derived-operators" - ], - "actions":[ - { - "name":"NewFile" - }, - { - "name":"aF" - }, + "actions": [ { - "name":"aB" + "name": "NewFile" }, { - "name":"TO_Msg" + "name": "SyncWait" }, { - "name":"TO_Ack" + "name": "TO_Ack" }, { - "name":"τ" + "name": "TO_Msg" }, { - "name":"SyncWait" + "name": "aA" }, { - "name":"aG" + "name": "aB" }, { - "name":"aA" - } - ], - "constants":[ - { - "name":"N", - "type":"int" + "name": "aF" }, { - "name":"MAX", - "type":"int" + "name": "aG" } ], - "variables":[ - { - "name":"s", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":6 - } - }, - { - "name":"srep", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":3 - } - }, - { - "name":"nrtr", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":"MAX" - } - }, - { - "name":"i", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":"N" - } - }, - { - "name":"bs", - "type":"bool" - }, - { - "name":"s_ab", - "type":"bool" - }, - { - "name":"fs", - "type":"bool" - }, - { - "name":"ls", - "type":"bool" - }, - { - "name":"r", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":5 - } - }, - { - "name":"rrep", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":4 - } - }, - { - "name":"fr", - "type":"bool" - }, - { - "name":"lr", - "type":"bool" - }, - { - "name":"br", - "type":"bool" - }, - { - "name":"r_ab", - "type":"bool" - }, - { - "name":"recv", - "type":"bool" - }, - { - "name":"T", - "type":"bool" - }, - { - "name":"k", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":2 - } - }, + "automata": [ { - "name":"l", - "type":{ - "kind":"bounded", - "base":"int", - "lower-bound":0, - "upper-bound":2 - } - }, - { - "name":"reward_", - "type":"real", - "initial-value":0.0, - "transient":true - } - ], - "restrict-initial":{ - "exp":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":0 - }, - "right":{ - "op":"=", - "left":"srep", - "right":0 - } - }, - "right":{ - "op":"=", - "left":"nrtr", - "right":0 - } - }, - "right":{ - "op":"=", - "left":"i", - "right":0 - } - }, - "right":{ - "op":"=", - "left":"bs", - "right":false - } - }, - "right":{ - "op":"=", - "left":"s_ab", - "right":false - } - }, - "right":{ - "op":"=", - "left":"fs", - "right":false - } - }, - "right":{ - "op":"=", - "left":"ls", - "right":false - } - }, - "right":{ - "op":"=", - "left":"r", - "right":0 - } - }, - "right":{ - "op":"=", - "left":"rrep", - "right":0 - } - }, - "right":{ - "op":"=", - "left":"fr", - "right":false - } - }, - "right":{ - "op":"=", - "left":"lr", - "right":false - } + "edges": [ + { + "destinations": [ + { + "assignments": [ + { + "ref": "s", + "value": 5 }, - "right":{ - "op":"=", - "left":"br", - "right":false + { + "ref": "srep", + "value": 1 } - }, - "right":{ - "op":"=", - "left":"r_ab", - "right":false - } - }, - "right":{ - "op":"=", - "left":"recv", - "right":false - } - }, - "right":{ - "op":"=", - "left":"T", - "right":false - } - }, - "right":{ - "op":"=", - "left":"k", - "right":0 - } - }, - "right":{ - "op":"=", - "left":"l", - "right":0 - } - } - }, - "properties":[ - { - "name":"Property_brp_0", - "expression":{ - "op":"filter", - "fun":"min", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"srep", - "right":1 + ], + "location": "l" + } + ], + "guard": { + "comment": "(((s = 3) & (nrtr = MAX)) & (i < N))", + "exp": { + "left": { + "left": { + "left": "s", + "op": "=", + "right": 3 }, - "right":{ - "op":"=", - "left":"rrep", - "right":3 + "op": "∧", + "right": { + "left": "nrtr", + "op": "=", + "right": "MAX" } }, - "right":"recv" - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F srep=1 & rrep=3 & recv ]" - }, - { - "name":"Property_brp_1", - "expression":{ - "op":"filter", - "fun":"max", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"srep", - "right":1 - }, - "right":{ - "op":"=", - "left":"rrep", - "right":3 - } - }, - "right":"recv" + "op": "∧", + "right": { + "left": "i", + "op": "<", + "right": "N" + } } - } + }, + "location": "l" }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F srep=1 & rrep=3 & recv ]" - }, - { - "name":"Property_brp_2", - "expression":{ - "op":"filter", - "fun":"min", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"srep", - "right":3 + { + "destinations": [ + { + "assignments": [ + { + "ref": "s", + "value": 5 }, - "right":{ - "op":"¬", - "exp":{ - "op":"=", - "left":"rrep", - "right":3 - } + { + "ref": "srep", + "value": 2 } - }, - "right":"recv" - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F srep=3 & !(rrep=3) & recv ]" - }, - { - "name":"Property_brp_3", - "expression":{ - "op":"filter", - "fun":"max", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"srep", - "right":3 + ], + "location": "l" + } + ], + "guard": { + "comment": "(((s = 3) & (nrtr = MAX)) & (i = N))", + "exp": { + "left": { + "left": { + "left": "s", + "op": "=", + "right": 3 }, - "right":{ - "op":"¬", - "exp":{ - "op":"=", - "left":"rrep", - "right":3 - } + "op": "∧", + "right": { + "left": "nrtr", + "op": "=", + "right": "MAX" } }, - "right":"recv" - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F srep=3 & !(rrep=3) & recv ]" - }, - { - "name":"Property_brp_4", - "expression":{ - "op":"filter", - "fun":"min", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"=", - "left":"s", - "right":5 - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F s=5 ]" - }, - { - "name":"Property_brp_5", - "expression":{ - "op":"filter", - "fun":"max", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"=", - "left":"s", - "right":5 - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F s=5 ]" - }, - { - "name":"Property_brp_6", - "expression":{ - "op":"filter", - "fun":"min", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":5 - }, - "right":{ - "op":"=", - "left":"srep", - "right":2 + "op": "∧", + "right": { + "left": "i", + "op": "=", + "right": "N" } } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F s=5 & srep=2 ]" - }, - { - "name":"Property_brp_7", - "expression":{ - "op":"filter", - "fun":"max", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":5 - }, - "right":{ - "op":"=", - "left":"srep", - "right":2 - } - } - } + }, + "location": "l" }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F s=5 & srep=2 ]" - }, - { - "name":"Property_brp_8", - "expression":{ - "op":"filter", - "fun":"min", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":5 + { + "destinations": [ + { + "assignments": [ + { + "ref": "s", + "value": 1 }, - "right":{ - "op":"=", - "left":"srep", - "right":1 + { + "ref": "i", + "value": { + "left": "i", + "op": "+", + "right": 1 + } } + ], + "location": "l" + } + ], + "guard": { + "comment": "((s = 4) & (i < N))", + "exp": { + "left": { + "left": "s", + "op": "=", + "right": 4 }, - "right":{ - "op":">", - "left":"i", - "right":8 + "op": "∧", + "right": { + "left": "i", + "op": "<", + "right": "N" } } - } + }, + "location": "l" }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F s=5 & srep=1 & i>8 ]" - }, - { - "name":"Property_brp_9", - "expression":{ - "op":"filter", - "fun":"max", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":5 + { + "destinations": [ + { + "assignments": [ + { + "ref": "s", + "value": 0 }, - "right":{ - "op":"=", - "left":"srep", - "right":1 - } - }, - "right":{ - "op":">", - "left":"i", - "right":8 - } - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F s=5 & srep=1 & i>8 ]" - }, - { - "name":"Property_brp_10", - "expression":{ - "op":"filter", - "fun":"min", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"¬", - "exp":{ - "op":"=", - "left":"srep", - "right":0 - } - }, - "right":{ - "op":"¬", - "exp":"recv" - } - } - } - }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F !(srep=0) & !recv ]" - }, - { - "name":"Property_brp_11", - "expression":{ - "op":"filter", - "fun":"max", - "values":{ - "op":"Pmax", - "exp":{ - "op":"U", - "left":true, - "right":{ - "op":"∧", - "left":{ - "op":"¬", - "exp":{ - "op":"=", - "left":"srep", - "right":0 + { + "ref": "srep", + "value": 3 } + ], + "location": "l" + } + ], + "guard": { + "comment": "((s = 4) & (i = N))", + "exp": { + "left": { + "left": "s", + "op": "=", + "right": 4 }, - "right":{ - "op":"¬", - "exp":"recv" + "op": "∧", + "right": { + "left": "i", + "op": "=", + "right": "N" } } - } + }, + "location": "l" }, - "states":{ - "op":"initial" - } - }, - "comment":"P=?[ F !(srep=0) & !recv ]" - } - ], - "automata":[ - { - "name":"sender", - "locations":[ - { - "name":"location" - } - ], - "initial-locations":[ - "location" - ], - "edges":[ { - "location":"location", - "action":"NewFile", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":0 - } - }, - "destinations":[ + "action": "NewFile", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":1 + "ref": "s", + "value": 1 }, { - "ref":"i", - "value":1 + "ref": "srep", + "value": 0 }, { - "ref":"srep", - "value":0 + "ref": "i", + "value": 1 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"aF", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":1 + ], + "guard": { + "comment": "(s = 0)", + "exp": { + "left": "s", + "op": "=", + "right": 0 } }, - "destinations":[ + "location": "l" + }, + { + "action": "SyncWait", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"s", - "value":2 - }, - { - "ref":"fs", - "value":{ - "op":"=", - "left":"i", - "right":1 - } - }, - { - "ref":"ls", - "value":{ - "op":"=", - "left":"i", - "right":"N" - } - }, - { - "ref":"bs", - "value":"s_ab" - }, - { - "ref":"nrtr", - "value":0 - }, + "assignments": [ { - "ref":"reward_", - "value":{ - "op":"ite", - "if":{ - "op":"=", - "left":"i", - "right":1 - }, - "then":1, - "else":0 - } + "ref": "s", + "value": 6 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"aB", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":2 + ], + "guard": { + "comment": "(s = 5)", + "exp": { + "left": "s", + "op": "=", + "right": 5 } }, - "destinations":[ + "location": "l" + }, + { + "action": "SyncWait", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":4 + "ref": "s_ab", + "value": false }, { - "ref":"s_ab", - "value":{ - "op":"¬", - "exp":"s_ab" - } + "ref": "s", + "value": 0 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"TO_Msg", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":2 + ], + "guard": { + "comment": "(s = 6)", + "exp": { + "left": "s", + "op": "=", + "right": 6 } }, - "destinations":[ + "location": "l" + }, + { + "action": "TO_Ack", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":3 + "ref": "s", + "value": 3 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"TO_Ack", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":2 + ], + "guard": { + "comment": "(s = 2)", + "exp": { + "left": "s", + "op": "=", + "right": 2 } }, - "destinations":[ + "location": "l" + }, + { + "action": "TO_Msg", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":3 + "ref": "s", + "value": 3 } - ] + ], + "location": "l" } - ] + ], + "guard": { + "comment": "(s = 2)", + "exp": { + "left": "s", + "op": "=", + "right": 2 + } + }, + "location": "l" }, { - "location":"location", - "action":"aF", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":3 - }, - "right":{ - "op":"<", - "left":"nrtr", - "right":"MAX" - } + "action": "aB", + "destinations": [ + { + "assignments": [ + { + "ref": "s_ab", + "value": { + "exp": "s_ab", + "op": "¬" + } + }, + { + "ref": "s", + "value": 4 + } + ], + "location": "l" + } + ], + "guard": { + "comment": "(s = 2)", + "exp": { + "left": "s", + "op": "=", + "right": 2 } }, - "destinations":[ + "location": "l" + }, + { + "action": "aF", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":2 + "ref": "bs", + "value": "s_ab" }, { - "ref":"fs", - "value":{ - "op":"=", - "left":"i", - "right":1 + "ref": "fs", + "value": { + "left": "i", + "op": "=", + "right": 1 } }, { - "ref":"ls", - "value":{ - "op":"=", - "left":"i", - "right":"N" + "ref": "ls", + "value": { + "left": "i", + "op": "=", + "right": "N" } }, { - "ref":"bs", - "value":"s_ab" - }, - { - "ref":"nrtr", - "value":{ - "op":"+", - "left":"nrtr", - "right":1 - } + "ref": "s", + "value": 2 }, { - "ref":"reward_", - "value":{ - "op":"ite", - "if":{ - "op":"=", - "left":"i", - "right":1 - }, - "then":1, - "else":0 - } + "ref": "nrtr", + "value": 0 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":3 - }, - "right":{ - "op":"=", - "left":"nrtr", - "right":"MAX" - } - }, - "right":{ - "op":"<", - "left":"i", - "right":"N" - } + ], + "guard": { + "comment": "(s = 1)", + "exp": { + "left": "s", + "op": "=", + "right": 1 } }, - "destinations":[ + "location": "l" + }, + { + "action": "aF", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":5 + "ref": "bs", + "value": "s_ab" }, { - "ref":"srep", - "value":1 - } - ] - } - ] - }, - { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":3 + "ref": "fs", + "value": { + "left": "i", + "op": "=", + "right": 1 + } }, - "right":{ - "op":"=", - "left":"nrtr", - "right":"MAX" - } - }, - "right":{ - "op":"=", - "left":"i", - "right":"N" - } - } - }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ { - "ref":"s", - "value":5 + "ref": "ls", + "value": { + "left": "i", + "op": "=", + "right": "N" + } }, { - "ref":"srep", - "value":2 - } - ] - } - ] - }, - { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":4 - }, - "right":{ - "op":"<", - "left":"i", - "right":"N" - } - } - }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"s", - "value":1 + "ref": "s", + "value": 2 }, { - "ref":"i", - "value":{ - "op":"+", - "left":"i", - "right":1 + "ref": "nrtr", + "value": { + "left": "nrtr", + "op": "+", + "right": 1 } } - ] - } - ] - }, - { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"=", - "left":"s", - "right":4 + ], + "location": "l" + } + ], + "guard": { + "comment": "((s = 3) & (nrtr < MAX))", + "exp": { + "left": { + "left": "s", + "op": "=", + "right": 3 }, - "right":{ - "op":"=", - "left":"i", - "right":"N" + "op": "∧", + "right": { + "left": "nrtr", + "op": "<", + "right": "MAX" } } }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"s", - "value":0 - }, - { - "ref":"srep", - "value":3 - } - ] - } - ] - }, + "location": "l" + } + ], + "initial-locations": [ + "l" + ], + "locations": [ { - "location":"location", - "action":"SyncWait", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":5 - } - }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"s", - "value":6 - } - ] - } - ] - }, + "name": "l" + } + ], + "name": "sender", + "variables": [] + }, + { + "edges": [ { - "location":"location", - "action":"SyncWait", - "guard":{ - "exp":{ - "op":"=", - "left":"s", - "right":6 - } - }, - "destinations":[ + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"s", - "value":0 + "ref": "r_ab", + "value": "br" }, { - "ref":"s_ab", - "value":false + "ref": "r", + "value": 2 } - ] + ], + "location": "l" } - ] - } - ] - }, - { - "name":"receiver", - "locations":[ - { - "name":"location" - } - ], - "initial-locations":[ - "location" - ], - "edges":[ - { - "location":"location", - "action":"SyncWait", - "guard":{ - "exp":{ - "op":"=", - "left":"r", - "right":0 + ], + "guard": { + "comment": "(r = 1)", + "exp": { + "left": "r", + "op": "=", + "right": 1 } }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"r", - "value":0 - } - ] - } - ] + "location": "l" }, { - "location":"location", - "action":"aG", - "guard":{ - "exp":{ - "op":"=", - "left":"r", - "right":0 - } - }, - "destinations":[ + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":1 + "ref": "r", + "value": 3 }, { - "ref":"fr", - "value":"fs" - }, - { - "ref":"lr", - "value":"ls" - }, - { - "ref":"br", - "value":"bs" + "ref": "rrep", + "value": 1 + } + ], + "location": "l" + } + ], + "guard": { + "comment": "((((r = 2) & (r_ab = br)) & (fr = true)) & (lr = false))", + "exp": { + "left": { + "left": { + "left": { + "left": "r", + "op": "=", + "right": 2 + }, + "op": "∧", + "right": { + "left": "r_ab", + "op": "=", + "right": "br" + } }, - { - "ref":"recv", - "value":"T" + "op": "∧", + "right": { + "left": "fr", + "op": "=", + "right": true } - ] + }, + "op": "∧", + "right": { + "left": "lr", + "op": "=", + "right": false + } } - ] + }, + "location": "l" }, { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"=", - "left":"r", - "right":1 - } - }, - "destinations":[ + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":2 + "ref": "r", + "value": 3 }, { - "ref":"r_ab", - "value":"br" + "ref": "rrep", + "value": 2 } - ] - } - ] - }, - { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"r", - "right":2 + ], + "location": "l" + } + ], + "guard": { + "comment": "((((r = 2) & (r_ab = br)) & (fr = false)) & (lr = false))", + "exp": { + "left": { + "left": { + "left": { + "left": "r", + "op": "=", + "right": 2 }, - "right":{ - "op":"=", - "left":"r_ab", - "right":"br" + "op": "∧", + "right": { + "left": "r_ab", + "op": "=", + "right": "br" } }, - "right":{ - "op":"=", - "left":"fr", - "right":true + "op": "∧", + "right": { + "left": "fr", + "op": "=", + "right": false } }, - "right":{ - "op":"=", - "left":"lr", - "right":false + "op": "∧", + "right": { + "left": "lr", + "op": "=", + "right": false } } }, - "destinations":[ + "location": "l" + }, + { + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":3 + "ref": "r", + "value": 3 }, { - "ref":"rrep", - "value":1 + "ref": "rrep", + "value": 3 } - ] - } - ] - }, - { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"r", - "right":2 + ], + "location": "l" + } + ], + "guard": { + "comment": "((((r = 2) & (r_ab = br)) & (fr = false)) & (lr = true))", + "exp": { + "left": { + "left": { + "left": { + "left": "r", + "op": "=", + "right": 2 }, - "right":{ - "op":"=", - "left":"r_ab", - "right":"br" + "op": "∧", + "right": { + "left": "r_ab", + "op": "=", + "right": "br" } }, - "right":{ - "op":"=", - "left":"fr", - "right":false + "op": "∧", + "right": { + "left": "fr", + "op": "=", + "right": false } }, - "right":{ - "op":"=", - "left":"lr", - "right":false + "op": "∧", + "right": { + "left": "lr", + "op": "=", + "right": true } } }, - "destinations":[ + "location": "l" + }, + { + "action": "SyncWait", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":3 - }, - { - "ref":"rrep", - "value":2 + "ref": "r", + "value": 0 } - ] + ], + "location": "l" } - ] + ], + "guard": { + "comment": "(r = 0)", + "exp": { + "left": "r", + "op": "=", + "right": 0 + } + }, + "location": "l" }, { - "location":"location", - "action":"τ", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"∧", - "left":{ - "op":"=", - "left":"r", - "right":2 - }, - "right":{ - "op":"=", - "left":"r_ab", - "right":"br" - } - }, - "right":{ - "op":"=", - "left":"fr", - "right":false + "action": "SyncWait", + "destinations": [ + { + "assignments": [ + { + "ref": "r", + "value": 5 } + ], + "location": "l" + } + ], + "guard": { + "comment": "((r = 4) & (ls = true))", + "exp": { + "left": { + "left": "r", + "op": "=", + "right": 4 }, - "right":{ - "op":"=", - "left":"lr", - "right":true + "op": "∧", + "right": { + "left": "ls", + "op": "=", + "right": true } } }, - "destinations":[ + "location": "l" + }, + { + "action": "SyncWait", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":3 + "ref": "r", + "value": 5 }, { - "ref":"rrep", - "value":3 + "ref": "rrep", + "value": 4 } - ] - } - ] - }, - { - "location":"location", - "action":"aA", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"=", - "left":"r", - "right":2 + ], + "location": "l" + } + ], + "guard": { + "comment": "((r = 4) & (ls = false))", + "exp": { + "left": { + "left": "r", + "op": "=", + "right": 4 }, - "right":{ - "op":"¬", - "exp":{ - "op":"=", - "left":"r_ab", - "right":"br" - } + "op": "∧", + "right": { + "left": "ls", + "op": "=", + "right": false } } }, - "destinations":[ + "location": "l" + }, + { + "action": "SyncWait", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ + { + "ref": "r", + "value": 0 + }, { - "ref":"r", - "value":4 + "ref": "rrep", + "value": 0 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"aA", - "guard":{ - "exp":{ - "op":"=", - "left":"r", - "right":3 + ], + "guard": { + "comment": "(r = 5)", + "exp": { + "left": "r", + "op": "=", + "right": 5 } }, - "destinations":[ + "location": "l" + }, + { + "action": "aA", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":4 + "ref": "r_ab", + "value": { + "exp": "r_ab", + "op": "¬" + } }, { - "ref":"r_ab", - "value":{ - "op":"¬", - "exp":"r_ab" - } + "ref": "r", + "value": 4 } - ] + ], + "location": "l" } - ] + ], + "guard": { + "comment": "(r = 3)", + "exp": { + "left": "r", + "op": "=", + "right": 3 + } + }, + "location": "l" }, { - "location":"location", - "action":"aG", - "guard":{ - "exp":{ - "op":"=", - "left":"r", - "right":4 + "action": "aA", + "destinations": [ + { + "assignments": [ + { + "ref": "r", + "value": 4 + } + ], + "location": "l" + } + ], + "guard": { + "comment": "((r = 2) & !((r_ab = br)))", + "exp": { + "left": { + "left": "r", + "op": "=", + "right": 2 + }, + "op": "∧", + "right": { + "exp": { + "left": "r_ab", + "op": "=", + "right": "br" + }, + "op": "¬" + } } }, - "destinations":[ + "location": "l" + }, + { + "action": "aG", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":2 + "ref": "fr", + "value": "fs" }, { - "ref":"fr", - "value":"fs" + "ref": "lr", + "value": "ls" }, { - "ref":"lr", - "value":"ls" + "ref": "br", + "value": "bs" }, { - "ref":"br", - "value":"bs" + "ref": "recv", + "value": "T" }, { - "ref":"recv", - "value":"T" + "ref": "r", + "value": 1 } - ] + ], + "location": "l" } - ] - }, - { - "location":"location", - "action":"SyncWait", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"=", - "left":"r", - "right":4 - }, - "right":{ - "op":"=", - "left":"ls", - "right":true - } + ], + "guard": { + "comment": "(r = 0)", + "exp": { + "left": "r", + "op": "=", + "right": 0 } }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"r", - "value":5 - } - ] - } - ] + "location": "l" }, { - "location":"location", - "action":"SyncWait", - "guard":{ - "exp":{ - "op":"∧", - "left":{ - "op":"=", - "left":"r", - "right":4 - }, - "right":{ - "op":"=", - "left":"ls", - "right":false - } - } - }, - "destinations":[ + "action": "aG", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"r", - "value":5 + "ref": "fr", + "value": "fs" }, { - "ref":"rrep", - "value":4 - } - ] - } - ] - }, - { - "location":"location", - "action":"SyncWait", - "guard":{ - "exp":{ - "op":"=", - "left":"r", - "right":5 - } - }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "ref": "lr", + "value": "ls" + }, + { + "ref": "br", + "value": "bs" + }, { - "ref":"r", - "value":0 + "ref": "recv", + "value": "T" }, { - "ref":"rrep", - "value":0 + "ref": "r", + "value": 2 } - ] + ], + "location": "l" } - ] + ], + "guard": { + "comment": "(r = 4)", + "exp": { + "left": "r", + "op": "=", + "right": 4 + } + }, + "location": "l" } - ] - }, - { - "name":"checker", - "locations":[ + ], + "initial-locations": [ + "l" + ], + "locations": [ { - "name":"location" + "name": "l" } ], - "initial-locations":[ - "location" - ], - "edges":[ + "name": "receiver", + "variables": [] + }, + { + "edges": [ { - "location":"location", - "action":"NewFile", - "guard":{ - "exp":{ - "op":"=", - "left":"T", - "right":false - } - }, - "destinations":[ + "action": "NewFile", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"T", - "value":true + "ref": "T", + "value": true } - ] + ], + "location": "l" } - ] + ], + "guard": { + "comment": "(T = false)", + "exp": { + "left": "T", + "op": "=", + "right": false + } + }, + "location": "l" } - ] - }, - { - "name":"channelK", - "locations":[ + ], + "initial-locations": [ + "l" + ], + "locations": [ { - "name":"location" + "name": "l" } ], - "initial-locations":[ - "location" - ], - "edges":[ + "name": "checker", + "variables": [] + }, + { + "edges": [ { - "location":"location", - "action":"aF", - "guard":{ - "exp":{ - "op":"=", - "left":"k", - "right":0 + "action": "TO_Msg", + "destinations": [ + { + "assignments": [ + { + "ref": "k", + "value": 0 + } + ], + "location": "l" + } + ], + "guard": { + "comment": "(k = 2)", + "exp": { + "left": "k", + "op": "=", + "right": 2 } }, - "destinations":[ + "location": "l" + }, + { + "action": "aF", + "destinations": [ { - "probability":{ - "exp":0.9800000 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"k", - "value":1 + "ref": "k", + "value": 1 } - ] + ], + "location": "l", + "probability": { + "comment": "49/50", + "exp": 0.98 + } }, { - "probability":{ - "exp":0.0200000 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"k", - "value":2 + "ref": "k", + "value": 2 } - ] + ], + "location": "l", + "probability": { + "comment": "1/50", + "exp": 0.02 + } } - ] - }, - { - "location":"location", - "action":"aG", - "guard":{ - "exp":{ - "op":"=", - "left":"k", - "right":1 + ], + "guard": { + "comment": "(k = 0)", + "exp": { + "left": "k", + "op": "=", + "right": 0 } }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"k", - "value":0 - } - ] - } - ] + "location": "l" }, { - "location":"location", - "action":"TO_Msg", - "guard":{ - "exp":{ - "op":"=", - "left":"k", - "right":2 - } - }, - "destinations":[ + "action": "aG", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"k", - "value":0 + "ref": "k", + "value": 0 } - ] + ], + "location": "l" + } + ], + "guard": { + "comment": "(k = 1)", + "exp": { + "left": "k", + "op": "=", + "right": 1 } - ] + }, + "location": "l" } - ] - }, - { - "name":"channelL", - "locations":[ + ], + "initial-locations": [ + "l" + ], + "locations": [ { - "name":"location" + "name": "l" } ], - "initial-locations":[ - "location" - ], - "edges":[ + "name": "channelK", + "variables": [] + }, + { + "edges": [ { - "location":"location", - "action":"aA", - "guard":{ - "exp":{ - "op":"=", - "left":"l", - "right":0 + "action": "TO_Ack", + "destinations": [ + { + "assignments": [ + { + "ref": "l", + "value": 0 + } + ], + "location": "l" + } + ], + "guard": { + "comment": "(l = 2)", + "exp": { + "left": "l", + "op": "=", + "right": 2 } }, - "destinations":[ + "location": "l" + }, + { + "action": "aA", + "destinations": [ { - "probability":{ - "exp":0.9900000 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"l", - "value":1 + "ref": "l", + "value": 1 } - ] + ], + "location": "l", + "probability": { + "comment": "99/100", + "exp": 0.99 + } }, { - "probability":{ - "exp":0.0100000 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"l", - "value":2 + "ref": "l", + "value": 2 } - ] + ], + "location": "l", + "probability": { + "comment": "1/100", + "exp": 0.01 + } } - ] - }, - { - "location":"location", - "action":"aB", - "guard":{ - "exp":{ - "op":"=", - "left":"l", - "right":1 + ], + "guard": { + "comment": "(l = 0)", + "exp": { + "left": "l", + "op": "=", + "right": 0 } }, - "destinations":[ - { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ - { - "ref":"l", - "value":0 - } - ] - } - ] + "location": "l" }, { - "location":"location", - "action":"TO_Ack", - "guard":{ - "exp":{ - "op":"=", - "left":"l", - "right":2 - } - }, - "destinations":[ + "action": "aB", + "destinations": [ { - "probability":{ - "exp":1 - }, - "location":"location", - "assignments":[ + "assignments": [ { - "ref":"l", - "value":0 + "ref": "l", + "value": 0 } - ] + ], + "location": "l" } - ] + ], + "guard": { + "comment": "(l = 1)", + "exp": { + "left": "l", + "op": "=", + "right": 1 + } + }, + "location": "l" } - ] + ], + "initial-locations": [ + "l" + ], + "locations": [ + { + "name": "l" + } + ], + "name": "channelL", + "variables": [] + } + ], + "constants": [ + { + "name": "N", + "type": "int" + }, + { + "name": "MAX", + "type": "int" } ], - "system":{ - "elements":[ + "features": [ + "derived-operators" + ], + "jani-version": 1, + "name": "jani_from_prism", + "properties": [], + "restrict-initial": { + "exp": true + }, + "system": { + "elements": [ { - "automaton":"sender" + "automaton": "sender" }, { - "automaton":"receiver" + "automaton": "receiver" }, { - "automaton":"checker" + "automaton": "checker" }, { - "automaton":"channelK" + "automaton": "channelK" }, { - "automaton":"channelL" + "automaton": "channelL" } ], - "syncs":[ + "syncs": [ { - "synchronise":[ - "aB", - null, + "result": "NewFile", + "synchronise": [ + "NewFile", null, + "NewFile", null, - "aB" - ], - "result":"aB" + null + ] }, { - "synchronise":[ - "TO_Ack", - null, + "result": "SyncWait", + "synchronise": [ + "SyncWait", + "SyncWait", null, null, - "TO_Ack" - ], - "result":"TO_Ack" + null + ] }, { - "synchronise":[ - null, - "aA", - null, + "result": "TO_Ack", + "synchronise": [ + "TO_Ack", null, - "aA" - ], - "result":"aA" - }, - { - "synchronise":[ - "aF", null, null, - "aF", - null - ], - "result":"aF" + "TO_Ack" + ] }, { - "synchronise":[ + "result": "TO_Msg", + "synchronise": [ "TO_Msg", null, null, "TO_Msg", null - ], - "result":"TO_Msg" + ] }, { - "synchronise":[ + "result": "aA", + "synchronise": [ null, - "aG", - null, - "aG", - null - ], - "result":"aG" - }, - { - "synchronise":[ - "NewFile", + "aA", null, - "NewFile", null, - null - ], - "result":"NewFile" + "aA" + ] }, { - "synchronise":[ - "SyncWait", - "SyncWait", + "result": "aB", + "synchronise": [ + "aB", null, null, - null - ], - "result":"SyncWait" + null, + "aB" + ] }, { - "synchronise":[ - "τ", - null, + "result": "aF", + "synchronise": [ + "aF", null, null, + "aF", null - ], - "result":"τ" + ] }, { - "synchronise":[ - null, - "τ", + "result": "aG", + "synchronise": [ null, + "aG", null, + "aG", null - ], - "result":"τ" + ] } ] - } + }, + "type": "dtmc", + "variables": [ + { + "initial-value": 0, + "name": "s", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": 6 + } + }, + { + "initial-value": 0, + "name": "srep", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": 3 + } + }, + { + "initial-value": 0, + "name": "nrtr", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": "MAX" + } + }, + { + "initial-value": 0, + "name": "i", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": "N" + } + }, + { + "initial-value": false, + "name": "bs", + "type": "bool" + }, + { + "initial-value": false, + "name": "s_ab", + "type": "bool" + }, + { + "initial-value": false, + "name": "fs", + "type": "bool" + }, + { + "initial-value": false, + "name": "ls", + "type": "bool" + }, + { + "initial-value": 0, + "name": "r", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": 5 + } + }, + { + "initial-value": 0, + "name": "rrep", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": 4 + } + }, + { + "initial-value": false, + "name": "fr", + "type": "bool" + }, + { + "initial-value": false, + "name": "lr", + "type": "bool" + }, + { + "initial-value": false, + "name": "br", + "type": "bool" + }, + { + "initial-value": false, + "name": "r_ab", + "type": "bool" + }, + { + "initial-value": false, + "name": "recv", + "type": "bool" + }, + { + "initial-value": false, + "name": "T", + "type": "bool" + }, + { + "initial-value": 0, + "name": "k", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": 2 + } + }, + { + "initial-value": 0, + "name": "l", + "type": { + "base": "int", + "kind": "bounded", + "lower-bound": 0, + "upper-bound": 2 + } + } + ] } From b90e061665f74931feb157125060be08a3cd0eb7 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 19 Jul 2018 16:58:26 +0200 Subject: [PATCH 074/147] Added missing include --- src/utility/chrono.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utility/chrono.cpp b/src/utility/chrono.cpp index def183e..eb34b14 100644 --- a/src/utility/chrono.cpp +++ b/src/utility/chrono.cpp @@ -1,6 +1,8 @@ #include "chrono.h" #include "src/helpers.h" +#include + void define_chrono(py::module& m) { py::class_(m, "milliseconds") .def("count", &std::chrono::milliseconds::count) From 2ab55a3b304d21c4801a4d46b661583aea53e778 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 19 Jul 2018 17:00:09 +0200 Subject: [PATCH 075/147] Fixed number in doc test --- doc/source/doc/reward_models.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/doc/reward_models.rst b/doc/source/doc/reward_models.rst index 8f12b20..8f53743 100644 --- a/doc/source/doc/reward_models.rst +++ b/doc/source/doc/reward_models.rst @@ -29,7 +29,7 @@ We can do model checking analogous to probabilities:: >>> initial_state = model.initial_states[0] >>> result = stormpy.model_checking(model, properties[0]) >>> print("Result: {}".format(result.at(initial_state))) - Result: 3.6666666666666665 + Result: 3.666666666666667 The reward model has a name which we can obtain as follows:: From 92b1ed72bc8ccae3bfec653f9e6c8693fb53b92e Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 1 Aug 2018 17:45:22 +0200 Subject: [PATCH 076/147] Adaptions to changes in Storm --- src/core/core.cpp | 1 + src/core/input.cpp | 1 + src/storage/prism.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 7b05c3b..b5021ae 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,6 +3,7 @@ #include "storm/utility/DirectEncodingExporter.h" #include "storm/storage/ModelFormulasPair.h" #include "storm/storage/dd/DdType.h" +#include "storm/storage/jani/Property.h" #include "storm/solver/OptimizationDirection.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm-parsers/api/storm-parsers.h" diff --git a/src/core/input.cpp b/src/core/input.cpp index baf51dc..23d696c 100644 --- a/src/core/input.cpp +++ b/src/core/input.cpp @@ -1,6 +1,7 @@ #include "input.h" #include "src/helpers.h" #include "storm-parsers/api/storm-parsers.h" +#include "storm/storage/jani/Property.h" void define_property(py::module& m) { py::class_(m, "Property", "Property") diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index 1f52288..b1b8a7c 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -20,7 +20,7 @@ void define_prism(py::module& m) { .def("simplify", &Program::simplify, "Simplify") .def("used_constants",&Program::usedConstants, "Compute Used Constants") .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") - .def("to_jani", &Program::toJaniWithLabelRenaming, "Transform to Jani program", py::arg("all_variables_global")=false, py::arg("suffix") = "") + .def("to_jani", &Program::toJaniWithLabelRenaming, "Transform to Jani program", py::arg("all_variables_global")=false, py::arg("suffix") = "", py::arg("standard_compliant")=false) .def("__str__", &streamToString); py::class_ module(m, "PrismModule", "A module in a Prism program"); From 4efdb3db8ceb0f9a8feffca370a0209ccc48856c Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 1 Aug 2018 17:59:57 +0200 Subject: [PATCH 077/147] Started extending DFT bindings --- CHANGELOG.md | 1 + src/dft/analysis.cpp | 25 ++++++++++++++++++++++++ src/dft/analysis.h | 5 +++++ src/dft/common.h | 1 + src/dft/dft.cpp | 43 +++++++++++++++++------------------------ src/dft/dft.h | 5 +---- src/dft/io.cpp | 18 +++++++++++++++++ src/dft/io.h | 6 ++++++ src/mod_dft.cpp | 5 +++++ tests/dft/test_build.py | 12 ++++-------- 10 files changed, 84 insertions(+), 37 deletions(-) create mode 100644 src/dft/analysis.cpp create mode 100644 src/dft/analysis.h create mode 100644 src/dft/io.cpp create mode 100644 src/dft/io.h diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e60a5..0e51bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Requires storm version >= 1.2.2 and pycarl version >= 2.0.2 * transforming symbolic to sparse models - Extraction of schedulers and queries on schedulers - Extended PLA bindings +- Extended DFT bindings - Extended documentation ### Version 1.2.0 diff --git a/src/dft/analysis.cpp b/src/dft/analysis.cpp new file mode 100644 index 0000000..3edbdf4 --- /dev/null +++ b/src/dft/analysis.cpp @@ -0,0 +1,25 @@ +#include "analysis.h" + +#include "storm-dft/parser/DFTJsonParser.h" +#include "storm-dft/builder/ExplicitDFTModelBuilder.h" +#include "storm-dft/storage/dft/DFTIsomorphism.h" + + +// Thin wrapper for DFT analysis +template +std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, bool enableDC) { + typename storm::modelchecker::DFTModelChecker::dft_results dftResults = storm::api::analyzeDFT(dft, properties, symred, allowModularisation, enableDC, false); + std::vector results; + for (auto result : dftResults) { + results.push_back(boost::get(result)); + } + return results; +} + + +// Define python bindings +void define_analysis(py::module& m) { + + m.def("analyze_dft", &analyzeDFT, "Analyze the DFT", py::arg("dft"), py::arg("properties"), py::arg("symred")=true, py::arg("allow_modularisation")=false, py::arg("enable_dont_care")=true); + +} diff --git a/src/dft/analysis.h b/src/dft/analysis.h new file mode 100644 index 0000000..102b3b9 --- /dev/null +++ b/src/dft/analysis.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +void define_analysis(py::module& m); diff --git a/src/dft/common.h b/src/dft/common.h index c9b9b0c..2de6bc0 100644 --- a/src/dft/common.h +++ b/src/dft/common.h @@ -1 +1,2 @@ #include "src/common.h" +#include "storm-dft/api/storm-dft.h" \ No newline at end of file diff --git a/src/dft/dft.cpp b/src/dft/dft.cpp index ccd2465..c9c06a4 100644 --- a/src/dft/dft.cpp +++ b/src/dft/dft.cpp @@ -1,34 +1,27 @@ #include "dft.h" -#include "storm-dft/parser/DFTJsonParser.h" -#include "storm-dft/builder/ExplicitDFTModelBuilder.h" +#include "src/helpers.h" +#include "storm-dft/storage/dft/DFT.h" #include "storm/settings/SettingsManager.h" #include "storm-dft/settings/modules/FaultTreeSettings.h" -#include "storm-dft/storage/dft/DFTIsomorphism.h" - -// Thin wrapper for model building using one formula as argument -template -std::shared_ptr> buildModelFromJsonDft(std::string const& jsonDft, bool symred) { - // Build DFT - storm::parser::DFTJsonParser parser; - storm::storage::DFT dft = parser.parseJson(jsonDft); - // Build model - std::map>> emptySymmetry; - storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); - if (symred) { - auto colouring = dft.colourDFT(); - symmetries = dft.findSymmetries(colouring); - } - storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, true); - typename storm::builder::ExplicitDFTModelBuilder::LabelOptions labeloptions({}, true); - builder.buildModel(labeloptions, 0, 0.0); - return builder.getModel(); -} + + +template using DFT = storm::storage::DFT; + void define_dft(py::module& m) { + m.def("_set_up", []() { storm::settings::addModule(); }, "Initialize Storm-dft"); - // Build model - m.def("build_sparse_model_from_json_dft", &buildModelFromJsonDft, "Build the model", py::arg("jsonDft"), py::arg("symred") = false); - m.def("build_sparse_parametric_model_from_json_dft", &buildModelFromJsonDft, "Build the parametric model", py::arg("jsonDft"), py::arg("symred") = false); + + + // DFT class + py::class_, std::shared_ptr>>(m, "DFT", "DFT") + .def("nr_elements", &DFT::nrElements) + ; + + py::class_, std::shared_ptr>>(m, "ParametricDFT", "Parametric DFT") + .def("nr_elements", &DFT::nrElements) + ; + } diff --git a/src/dft/dft.h b/src/dft/dft.h index 2dc129a..147b75a 100644 --- a/src/dft/dft.h +++ b/src/dft/dft.h @@ -1,8 +1,5 @@ -#ifndef PYTHON_DFT_DFT_H_ -#define PYTHON_DFT_DFT_H_ +#pragma once #include "common.h" void define_dft(py::module& m); - -#endif /* PYTHON_DFT_DFT_H_ */ diff --git a/src/dft/io.cpp b/src/dft/io.cpp new file mode 100644 index 0000000..6c56334 --- /dev/null +++ b/src/dft/io.cpp @@ -0,0 +1,18 @@ +#include "io.h" +#include "src/helpers.h" + + +// Define python bindings +void define_input(py::module& m) { + + // Load DFT input + m.def("load_dft_galileo", &storm::api::loadDFTGalileo, "Load DFT from Galileo file", py::arg("path")); + // Parse Jani model + m.def("load_dft_json", &storm::api::loadDFTJson, "Load DFT from JSON file", py::arg("path")); +} + +void define_output(py::module& m) { + + // Export DFT + m.def("export_dft_json", &storm::api::exportDFTToJson, "Export DFT to JSON file", py::arg("dft"), py::arg("path")); +} diff --git a/src/dft/io.h b/src/dft/io.h new file mode 100644 index 0000000..663b9e3 --- /dev/null +++ b/src/dft/io.h @@ -0,0 +1,6 @@ +#pragma once + +#include "common.h" + +void define_input(py::module& m); +void define_output(py::module& m); diff --git a/src/mod_dft.cpp b/src/mod_dft.cpp index 4d840eb..315cc7c 100644 --- a/src/mod_dft.cpp +++ b/src/mod_dft.cpp @@ -1,6 +1,8 @@ #include "common.h" #include "dft/dft.h" +#include "dft/io.h" +#include "dft/analysis.h" PYBIND11_MODULE(dft, m) { m.doc() = "Functionality for DFT analysis"; @@ -11,4 +13,7 @@ PYBIND11_MODULE(dft, m) { #endif define_dft(m); + define_input(m); + define_output(m); + define_analysis(m); } diff --git a/tests/dft/test_build.py b/tests/dft/test_build.py index e09bc66..09fcc3f 100644 --- a/tests/dft/test_build.py +++ b/tests/dft/test_build.py @@ -9,12 +9,8 @@ from configurations import dft @dft class TestBuild: def test_build_dft(self): - model = stormpy.dft.build_sparse_model_from_json_dft(get_example_path("dft", "and.json")) + dft = stormpy.dft.load_dft_json(get_example_path("dft", "and.json")) formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") - assert model.nr_states == 4 - assert model.nr_transitions == 5 - assert len(model.initial_states) == 1 - initial_state = model.initial_states[0] - assert initial_state == 1 - result = stormpy.model_checking(model, formulas[0]) - assert math.isclose(result.at(initial_state), 3) + assert dft.nr_elements() == 3 + results = stormpy.dft.analyze_dft(dft, [formulas[0].raw_formula]) + assert math.isclose(results[0], 3) From eb3c7e88496492e3415530e0c0b4233a64014344 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 20 Jul 2018 14:02:00 +0200 Subject: [PATCH 078/147] Get Choice Labeling --- src/storage/labeling.cpp | 3 +++ src/storage/model.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/src/storage/labeling.cpp b/src/storage/labeling.cpp index 8b9ef67..25aa4ba 100644 --- a/src/storage/labeling.cpp +++ b/src/storage/labeling.cpp @@ -3,6 +3,7 @@ #include "storm/models/sparse/ItemLabeling.h" #include "storm/models/sparse/StateLabeling.h" +#include "storm/models/sparse/ChoiceLabeling.h" // Define python bindings void define_labeling(py::module& m) { @@ -30,4 +31,6 @@ void define_labeling(py::module& m) { .def("__str__", &streamToString) ; + + py::class_(m, "ChoiceLabeling", "Labeling for choices", labeling); } diff --git a/src/storage/model.cpp b/src/storage/model.cpp index f2f49a1..99995bb 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -164,6 +164,7 @@ void define_sparse_model(py::module& m) { // Models with double numbers py::class_, std::shared_ptr>, ModelBase> model(m, "_SparseModel", "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix"); model.def_property_readonly("labeling", &getLabeling, "Labels") + .def_property_readonly("choice_labeling", [](SparseModel const& model) {return model.getChoiceLabeling();}, "get choice labelling") .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") .def_property_readonly("initial_states", &getSparseInitialStates, "Initial states") .def_property_readonly("states", [](SparseModel& model) { From 953c1551dc235fd488cb24ab501ba89db247435e Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 23 Jul 2018 11:05:35 +0200 Subject: [PATCH 079/147] access reward names in reward operator --- src/logic/formulae.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index 8b80dea..05981a3 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -72,7 +72,9 @@ void define_formulae(py::module& m) { py::class_>(m, "TimeOperator", "The time operator", operatorFormula); py::class_>(m, "LongRunAvarageOperator", "Long run average operator", operatorFormula); py::class_>(m, "ProbabilityOperator", "Probability operator", operatorFormula); - py::class_>(m, "RewardOperator", "Reward operator", operatorFormula); + py::class_>(m, "RewardOperator", "Reward operator", operatorFormula) + .def("has_reward_name", &storm::logic::RewardOperatorFormula::hasRewardModelName) + .def_property_readonly("reward_name", &storm::logic::RewardOperatorFormula::getRewardModelName); py::class_> binaryStateFormula(m, "BinaryStateFormula", "State formula with two operands", stateFormula); py::class_>(m, "BooleanBinaryStateFormula", "Boolean binary state formula", binaryStateFormula); } From 134eae574187349ed6cb3c3a388feb9ffb0cdf9a Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 1 Aug 2018 22:44:03 +0200 Subject: [PATCH 080/147] extend capabilities for formulae --- src/logic/formulae.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index 05981a3..90dc80b 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -17,6 +17,7 @@ void define_formulae(py::module& m) { py::class_> formula(m, "Formula", "Generic Storm Formula"); formula.def("__str__", &storm::logic::Formula::toString) .def("clone", [](storm::logic::Formula const& f) { storm::logic::CloneVisitor cv; return cv.clone(f);}) + .def("substitute", [](storm::logic::Formula const& f, std::map const& map) { return f.substitute(map); }, "Substitute variables", py::arg("constants_map")) .def("substitute_labels_by_labels", [](storm::logic::Formula const& f, std::map const& labelSubs) {storm::logic::LabelSubstitutionVisitor lsv(labelSubs); return lsv.substitute(f);}, "substitute label occurences", py::arg("replacements")) .def_property_readonly("is_probability_operator", &storm::logic::Formula::isProbabilityOperatorFormula, "is it a probability operator") .def_property_readonly("is_reward_operator", &storm::logic::Formula::isRewardOperatorFormula, "is it a reward operator") @@ -57,12 +58,20 @@ void define_formulae(py::module& m) { operatorFormula.def_property_readonly("has_bound", &storm::logic::OperatorFormula::hasBound, "Flag if formula is bounded") .def_property("comparison_type", &storm::logic::OperatorFormula::getComparisonType, &storm::logic::OperatorFormula::setComparisonType, "Comparison type of bound") .def_property_readonly("threshold", [](storm::logic::OperatorFormula const& f) { - if (!f.getThreshold().hasRationalType()) { - throw std::runtime_error("Can't get non-rational threshold (not implemented)"); - } else { + if (f.getThreshold().containsVariables()) { + throw std::runtime_error("To obtain the threshold as an expression, use threshold_expr"); + } + if (f.getThreshold().hasRationalType()) { return f.getThresholdAs(); + } else if (f.getThreshold().hasIntegerType()) { + return storm::utility::convertNumber(f.getThreshold().evaluateAsInt()); + } else { + throw std::runtime_error("Can't get non-rational threshold (not implemented)"); } }, "Threshold of bound (currently only applicable to rational expressions)") + .def_property_readonly("threshold_expr", [](storm::logic::OperatorFormula const& f) { + return f.getThreshold(); + }) .def("set_bound", [](storm::logic::OperatorFormula& f, storm::logic::ComparisonType comparisonType, storm::expressions::Expression const& bound) { f.setBound(storm::logic::Bound(comparisonType, bound)); }, "Set bound", py::arg("comparison_type"), py::arg("bound")) From acb1004c04ac9b46cea2e27cdb17a3b10e8598c2 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 1 Aug 2018 22:44:45 +0200 Subject: [PATCH 081/147] choice origins --- src/mod_storage.cpp | 2 ++ src/storage/choiceorigins.cpp | 13 +++++++++++++ src/storage/choiceorigins.h | 6 ++++++ src/storage/model.cpp | 2 ++ 4 files changed, 23 insertions(+) create mode 100644 src/storage/choiceorigins.cpp create mode 100644 src/storage/choiceorigins.h diff --git a/src/mod_storage.cpp b/src/mod_storage.cpp index e85dd03..997d037 100644 --- a/src/mod_storage.cpp +++ b/src/mod_storage.cpp @@ -8,6 +8,7 @@ #include "storage/prism.h" #include "storage/jani.h" #include "storage/state.h" +#include "storage/choiceorigins.h" #include "storage/labeling.h" #include "storage/expressions.h" @@ -30,6 +31,7 @@ PYBIND11_MODULE(storage, m) { define_prism(m); define_jani(m); define_labeling(m); + define_origins(m); define_expressions(m); define_scheduler(m, "Double"); define_distribution(m, "Double"); diff --git a/src/storage/choiceorigins.cpp b/src/storage/choiceorigins.cpp new file mode 100644 index 0000000..c41cfe2 --- /dev/null +++ b/src/storage/choiceorigins.cpp @@ -0,0 +1,13 @@ +#include "choiceorigins.h" +#include "storm/storage/sparse/JaniChoiceOrigins.h" +#include "storm/storage/jani/Model.h" + +void define_origins(py::module& m) { + using ChoiceOrigins = storm::storage::sparse::ChoiceOrigins; + py::class_> co(m, "ChoiceOrigins", "This class represents the origin of choices of a model in terms of the input model spec."); + using JaniChoiceOrigins = storm::storage::sparse::JaniChoiceOrigins; + py::class_>(m, "JaniChoiceOrigins", "This class represents for each choice the origin in the jani spec.") + .def_property_readonly("model", &JaniChoiceOrigins::getModel, "retrieves the associated JANI model") + .def("get_edge_index_set", [](JaniChoiceOrigins const& co, uint64_t choice) { return co.getEdgeIndexSet(choice); }, "returns the set of edges that induced the choice", py::arg("choice_index")) + ; +} \ No newline at end of file diff --git a/src/storage/choiceorigins.h b/src/storage/choiceorigins.h new file mode 100644 index 0000000..5082bb9 --- /dev/null +++ b/src/storage/choiceorigins.h @@ -0,0 +1,6 @@ +#pragma once + +#include "common.h" + + +void define_origins(py::module& m); \ No newline at end of file diff --git a/src/storage/model.cpp b/src/storage/model.cpp index 99995bb..7844dce 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -165,6 +165,8 @@ void define_sparse_model(py::module& m) { py::class_, std::shared_ptr>, ModelBase> model(m, "_SparseModel", "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix"); model.def_property_readonly("labeling", &getLabeling, "Labels") .def_property_readonly("choice_labeling", [](SparseModel const& model) {return model.getChoiceLabeling();}, "get choice labelling") + .def("has_choice_origins", [](SparseModel const& model) {return model.hasChoiceOrigins();}, "has choice origins?") + .def_property_readonly("choice_origins", [](SparseModel const& model) {return model.getChoiceOrigins();}) .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") .def_property_readonly("initial_states", &getSparseInitialStates, "Initial states") .def_property_readonly("states", [](SparseModel& model) { From a7cc7b3086974585415a46fabfd7576f82ecf5ac Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Mon, 6 Aug 2018 13:59:11 +0200 Subject: [PATCH 082/147] Extended bindings for DFT class --- lib/stormpy/examples/files/dft/hecs.dft | 24 ++++++++++++++++++++++++ src/dft/dft.cpp | 12 ++++++++++-- tests/dft/test_build.py | 16 +++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 lib/stormpy/examples/files/dft/hecs.dft diff --git a/lib/stormpy/examples/files/dft/hecs.dft b/lib/stormpy/examples/files/dft/hecs.dft new file mode 100644 index 0000000..b470ff9 --- /dev/null +++ b/lib/stormpy/examples/files/dft/hecs.dft @@ -0,0 +1,24 @@ +toplevel "n0"; +"n0" or "n116" "n137" "n120" "n21"; +"n137" wsp "n139" "n9"; +"n120" wsp "n118" "n134" "n10"; +"n21" 3of5 "n111" "n109" "n117" "n110" "n112"; +"n110" or "n18" "n7"; +"n117" or "n17" "n7"; +"n109" or "n15" "n23"; +"n112" or "n19" "n13"; +"n13" and "n23" "n7"; +"n111" or "n14" "n23"; +"n116" lambda=0.0018 dorm=0.0; +"n139" lambda=2.0E-5 dorm=0.0; +"n10" lambda=0.001 dorm=0.0; +"n118" lambda=0.002 dorm=0.0; +"n18" lambda=6.0E-4 dorm=0.0; +"n17" lambda=6.0E-4 dorm=0.0; +"n15" lambda=6.0E-4 dorm=0.0; +"n19" lambda=6.0E-4 dorm=0.0; +"n7" lambda=5.0E-4 dorm=0.0; +"n23" lambda=5.0E-4 dorm=0.0; +"n14" lambda=6.0E-4 dorm=0.0; +"n134" lambda=0.002 dorm=0.0; +"n9" lambda=1.0E-5 dorm=0.0; diff --git a/src/dft/dft.cpp b/src/dft/dft.cpp index c9c06a4..7af27d4 100644 --- a/src/dft/dft.cpp +++ b/src/dft/dft.cpp @@ -17,11 +17,19 @@ void define_dft(py::module& m) { // DFT class py::class_, std::shared_ptr>>(m, "DFT", "DFT") - .def("nr_elements", &DFT::nrElements) + .def("nr_elements", &DFT::nrElements, "Total number of elements") + .def("nr_be", &DFT::nrBasicElements, "Number of basic elements") + .def("nr_dynamic", &DFT::nrDynamicElements, "Number of dynamic elements") + .def("can_have_nondeterminism", &DFT::canHaveNondeterminism, "Whether the model can contain non-deterministic choices") + .def("__str__", &DFT::getInfoString) ; py::class_, std::shared_ptr>>(m, "ParametricDFT", "Parametric DFT") - .def("nr_elements", &DFT::nrElements) + .def("nr_elements", &DFT::nrElements, "Total number of elements") + .def("nr_be", &DFT::nrBasicElements, "Number of basic elements") + .def("nr_dynamic", &DFT::nrDynamicElements, "Number of dynamic elements") + .def("can_have_nondeterminism", &DFT::canHaveNondeterminism, "Whether the model can contain non-deterministic choices") + .def("__str__", &DFT::getInfoString) ; } diff --git a/tests/dft/test_build.py b/tests/dft/test_build.py index 09fcc3f..d9a84fe 100644 --- a/tests/dft/test_build.py +++ b/tests/dft/test_build.py @@ -8,7 +8,21 @@ from configurations import dft @dft class TestBuild: - def test_build_dft(self): + def test_load_dft_json(self): + dft = stormpy.dft.load_dft_json(get_example_path("dft", "and.json")) + assert dft.nr_elements() == 3 + assert dft.nr_be() == 2 + assert dft.nr_dynamic() == 0 + assert not dft.can_have_nondeterminism() + + def test_load_dft_galileo(self): + dft = stormpy.dft.load_dft_galileo(get_example_path("dft", "hecs.dft")) + assert dft.nr_elements() == 23 + assert dft.nr_be() == 13 + assert dft.nr_dynamic() == 2 + assert not dft.can_have_nondeterminism() + + def test_analyze_mttf(self): dft = stormpy.dft.load_dft_json(get_example_path("dft", "and.json")) formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") assert dft.nr_elements() == 3 From 7821e72dac7e3bc25d48dbf6f8e060aacbbe46d0 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Mon, 6 Aug 2018 14:12:22 +0200 Subject: [PATCH 083/147] Fixed path in documentation --- doc/source/doc/reward_models.rst | 2 +- doc/source/doc/shortest_paths.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/doc/reward_models.rst b/doc/source/doc/reward_models.rst index 8f53743..e2b1109 100644 --- a/doc/source/doc/reward_models.rst +++ b/doc/source/doc/reward_models.rst @@ -2,7 +2,7 @@ Reward Models ************** -In :doc:`getting_started`, we mainly looked at probabilities in the Markov models and properties that refer to these probabilities. +In :doc:`../getting_started`, we mainly looked at probabilities in the Markov models and properties that refer to these probabilities. In this section, we discuss reward models. Exploring reward models diff --git a/doc/source/doc/shortest_paths.rst b/doc/source/doc/shortest_paths.rst index 95e687e..aca96fa 100644 --- a/doc/source/doc/shortest_paths.rst +++ b/doc/source/doc/shortest_paths.rst @@ -20,7 +20,7 @@ Examining Shortest Paths .. seealso:: `07-shortest-paths.py `_ -As in :doc:`getting_started`, we import some required modules and build a model from the example files:: +As in :doc:`../getting_started`, we import some required modules and build a model from the example files:: >>> import stormpy.examples >>> import stormpy.examples.files From 8dfd0b4332233c8911c757fa66ae3150514627d2 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Mon, 6 Aug 2018 15:19:15 +0200 Subject: [PATCH 084/147] First part of DFT documentation --- doc/source/advanced_topics.rst | 3 ++- doc/source/doc/dfts.rst | 48 ++++++++++++++++++++++++++++++++++ doc/source/getting_started.rst | 1 + examples/dfts/01-dfts.py | 29 ++++++++++++++++++++ lib/stormpy/examples/files.py | 6 ++++- 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 doc/source/doc/dfts.rst create mode 100644 examples/dfts/01-dfts.py diff --git a/doc/source/advanced_topics.rst b/doc/source/advanced_topics.rst index 765149a..dc4a8e3 100644 --- a/doc/source/advanced_topics.rst +++ b/doc/source/advanced_topics.rst @@ -11,4 +11,5 @@ This guide is a collection of examples meant to bridge the gap between the getti doc/building_models doc/reward_models doc/shortest_paths - doc/parametric_models \ No newline at end of file + doc/parametric_models + doc/dfts \ No newline at end of file diff --git a/doc/source/doc/dfts.rst b/doc/source/doc/dfts.rst new file mode 100644 index 0000000..c143a2d --- /dev/null +++ b/doc/source/doc/dfts.rst @@ -0,0 +1,48 @@ +******************* +Dynamic Fault Trees +******************* + + +Building DFTs +============= +.. seealso:: `01-dfts.py `_ + +Dynamic fault trees can be loaded from either the Galileo format via ``load_dft_json(path)`` or from a custom JSON format via ``load_dft_galileo(path)``. +We start by loading a simple DFT containing an AND gate from JSON:: + + >>> import stormpy + >>> import stormpy.dft + >>> import stormpy.examples + >>> import stormpy.examples.files + >>> path_json = stormpy.examples.files.dft_json_and + >>> dft_small = stormpy.dft.load_dft_json(path_json) + >>> print(dft_small) + Top level index: 2, Nr BEs2 + +Next we load a more complex DFT from the Galileo format:: + + >>> path_galileo = stormpy.examples.files.dft_galileo_hecs + >>> dft = stormpy.dft.load_dft_galileo(path_galileo) + +After loading the DFT, we can display some common statistics about the model:: + + >>> print("DFT with {} elements.".format(dft.nr_elements())) + DFT with 23 elements. + >>> print("DFT has {} BEs and {} dynamic elements.".format(dft.nr_be(), dft.nr_dynamic())) + DFT has 13 BEs and 2 dynamic elements. + + +Analyzing DFTs +============== +.. seealso:: `01-dfts.py `_ + +The next step is to analyze the DFT via ``analyze_dft(dft, formula)``. +Here we can use all standard properties as described in :ref:`getting-started-building-properties`. +In our example we compute the `Mean-time-to-failure (MTTF)`:: + + >>> formula_str = "T=? [ F \"failed\" ]" + >>> formulas = stormpy.parse_properties(formula_str) + >>> results = stormpy.dft.analyze_dft(dft, [formulas[0].raw_formula]) + >>> result = results[0] + >>> print("MTTF: {:.2f}".format(result)) + MTTF: 363.89 diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index 947036a..abf1c57 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -58,6 +58,7 @@ Moreover, initial states and deadlocks are indicated with a labelling function. We will investigate ways to examine the model in more detail in :ref:`getting-started-investigating-the-model`. +.. _getting-started-building-properties: Building properties -------------------------- diff --git a/examples/dfts/01-dfts.py b/examples/dfts/01-dfts.py new file mode 100644 index 0000000..691d150 --- /dev/null +++ b/examples/dfts/01-dfts.py @@ -0,0 +1,29 @@ +import stormpy +import stormpy.dft + +import stormpy.examples +import stormpy.examples.files + + +def example_dfts_01(): + # Load from JSON + path_json = stormpy.examples.files.dft_json_and + dft_small = stormpy.dft.load_dft_json(path_json) + print(dft_small) + + # Load from Galileo + path = stormpy.examples.files.dft_galileo_hecs + dft = stormpy.dft.load_dft_galileo(path) + print("DFT with {} elements.".format(dft.nr_elements())) + print("DFT has {} BEs and {} dynamic elements.".format(dft.nr_be(), dft.nr_dynamic())) + + # Analyze + formula_str = "T=? [ F \"failed\" ]" + formulas = stormpy.parse_properties(formula_str) + results = stormpy.dft.analyze_dft(dft, [formulas[0].raw_formula]) + result = results[0] + print(result) + + +if __name__ == '__main__': + example_dfts_01() diff --git a/lib/stormpy/examples/files.py b/lib/stormpy/examples/files.py index 61d9fdb..cb4718d 100644 --- a/lib/stormpy/examples/files.py +++ b/lib/stormpy/examples/files.py @@ -26,4 +26,8 @@ drn_pdtmc_die = _path("pdtmc", "die.drn") jani_dtmc_die = _path("dtmc", "die.jani") """Jani Version of Knuth Yao Die Example""" prism_mdp_coin_2_2 = _path("mdp", "coin2-2.nm") -"""Prism example for coin MDP""" \ No newline at end of file +"""Prism example for coin MDP""" +dft_galileo_hecs = _path("dft", "hecs.dft") +"""DFT example for HECS (Galileo format)""" +dft_json_and = _path("dft", "and.json") +"""DFT example for AND gate (JSON format)""" From 944b5bd01c4c619fc7e73dc9f4da08f8f712c8eb Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 7 Aug 2018 11:04:50 +0200 Subject: [PATCH 085/147] Updated test as TimeOperatorFormulas are now supported in Storm --- tests/core/test_modelchecking.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/tests/core/test_modelchecking.py b/tests/core/test_modelchecking.py index 9e82b9f..a750c74 100644 --- a/tests/core/test_modelchecking.py +++ b/tests/core/test_modelchecking.py @@ -33,7 +33,8 @@ class TestModelChecking: def test_model_checking_jani_dtmc(self): jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "die.jani")) formula = properties["Probability to throw a six"] - model = stormpy.build_model(jani_model, [formula]) + formula2 = properties["Expected number of coin flips"] + model = stormpy.build_model(jani_model, [formula, formula2]) assert model.nr_states == 13 assert model.nr_transitions == 20 assert len(model.initial_states) == 1 @@ -41,19 +42,8 @@ class TestModelChecking: assert initial_state == 0 result = stormpy.model_checking(model, formula) assert math.isclose(result.at(initial_state), 1 / 6) - - def test_model_checking_jani_dtmc(self): - jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "die.jani")) - formula = properties["Expected number of coin flips"] - model = stormpy.build_model(jani_model, [formula]) - assert model.nr_states == 13 - assert model.nr_transitions == 20 - assert len(model.initial_states) == 1 - initial_state = model.initial_states[0] - assert initial_state == 0 - # Unsupported formula yields None result - result = stormpy.model_checking(model, formula) - assert result is None + result = stormpy.model_checking(model, formula2) + assert math.isclose(result.at(initial_state), 11 / 3) def test_model_checking_dtmc_all_labels(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) From 653144d415006f69a8a29d808a44eaac6dc9d7f3 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 7 Aug 2018 21:12:35 -0700 Subject: [PATCH 086/147] counterexample support updated --- src/core/counterexample.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index e92445c..b3eb054 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -23,7 +23,9 @@ void define_counterexamples(py::module& m) { .def_readonly("analysis_time", &CexGeneratorStats::analysisTime) .def_readonly("setup_time", &CexGeneratorStats::setupTime) .def_readonly("model_checking_time", &CexGeneratorStats::modelCheckingTime) - .def_readonly("solver_time", &CexGeneratorStats::solverTime); + .def_readonly("solver_time", &CexGeneratorStats::solverTime) + .def_readonly("cut_time", &CexGeneratorStats::cutTime) + .def_readonly("iterations", &CexGeneratorStats::iterations); using CexGeneratorOptions = SMTMinimalLabelSetGenerator::Options; py::class_(m, "SMTCounterExampleGeneratorOptions", "Options for highlevel counterexample generation") @@ -31,10 +33,11 @@ void define_counterexamples(py::module& m) { .def_readwrite("check_threshold_feasible", &CexGeneratorOptions::checkThresholdFeasible) .def_readwrite("encode_reachability", &CexGeneratorOptions::encodeReachability) .def_readwrite("silent", &CexGeneratorOptions::silent) + .def_readwrite("add_backward_implication_cuts", &CexGeneratorOptions::addBackwardImplicationCuts) .def_readwrite("use_dynamic_constraints", &CexGeneratorOptions::useDynamicConstraints) .def_readwrite("maximum_counterexamples", &CexGeneratorOptions::maximumCounterexamples) .def_readwrite("continue_after_first_counterexample", &CexGeneratorOptions::continueAfterFirstCounterexampleUntil) - + .def_readwrite("maximum_iterations_after_counterexample", &CexGeneratorOptions::maximumExtraIterations) ; py::class_>(m, "SMTCounterExampleGenerator", "Highlevel Counterexample Generator with SMT as backend"). def_static("precompute", &SMTMinimalLabelSetGenerator::precompute, "Precompute input for counterexample generation", py::arg("env"), py::arg("symbolic_model"), py::arg("model"), py::arg("formula")). From c9d4584dd4dc169bf10ce3a82a96962a327e27b9 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 7 Aug 2018 21:12:59 -0700 Subject: [PATCH 087/147] some support for the solver environment --- src/core/environment.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/core/environment.cpp b/src/core/environment.cpp index 23ad447..6e6f8f8 100644 --- a/src/core/environment.cpp +++ b/src/core/environment.cpp @@ -1,10 +1,33 @@ #include "environment.h" #include "src/helpers.h" #include "storm/environment/Environment.h" +#include "storm/environment/solver/SolverEnvironment.h" +#include "storm/environment/solver/EigenSolverEnvironment.h" +#include "storm/environment/solver/GmmxxSolverEnvironment.h" +#include "storm/environment/solver/GameSolverEnvironment.h" +#include "storm/environment/solver/NativeSolverEnvironment.h" +#include "storm/environment/solver/TopologicalSolverEnvironment.h" +#include "storm/environment/solver/MultiplierEnvironment.h" +#include "storm/environment/solver/MinMaxSolverEnvironment.h" void define_environment(py::module& m) { + py::enum_(m, "EquationSolverType", "Solver type for equation systems") + .value("native", storm::solver::EquationSolverType::Native) + .value("eigen", storm::solver::EquationSolverType::Eigen) + .value("elimination", storm::solver::EquationSolverType::Elimination) + .value("gmmxx", storm::solver::EquationSolverType::Gmmxx) + .value("topological", storm::solver::EquationSolverType::Topological) + ; + py::class_(m, "Environment", "Environment") .def(py::init<>(), "Construct default environment") + .def_property_readonly("solver_environment", [](storm::Environment& env) {return env.solver();}, "solver part of environment") ; + + py::class_(m, "SolverEnvironment", "Environment for solvers") + .def("set_force_sound", &storm::SolverEnvironment::setForceSoundness, "force soundness", py::arg("new_value") = true) + .def("set_linear_equation_solver_type", &storm::SolverEnvironment::setLinearEquationSolverType, "set solver type to use", py::arg("new_value"), py::arg("set_from_default") = false) + ; + } From 1308fe2e931e3b6b426a4b53f98c28b8e3665899 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 8 Aug 2018 13:48:00 +0200 Subject: [PATCH 088/147] Changes according to DFT loading in Storm --- doc/source/doc/dfts.rst | 8 +++++--- examples/dfts/01-dfts.py | 4 ++-- src/dft/io.cpp | 8 +++++--- tests/dft/test_build.py | 6 +++--- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/doc/source/doc/dfts.rst b/doc/source/doc/dfts.rst index c143a2d..d46d821 100644 --- a/doc/source/doc/dfts.rst +++ b/doc/source/doc/dfts.rst @@ -7,7 +7,9 @@ Building DFTs ============= .. seealso:: `01-dfts.py `_ -Dynamic fault trees can be loaded from either the Galileo format via ``load_dft_json(path)`` or from a custom JSON format via ``load_dft_galileo(path)``. +Dynamic fault trees can be loaded from either the Galileo format or from a custom JSON form. +A file containing the DFT in the Galileo format can be loaded via ``load_dft_galileo_file(path)``. +The custom JSON format can be loaded from a file via ``load_dft_json_file(path)`` or directly from a string via ``load_dft_json_string(path)``. We start by loading a simple DFT containing an AND gate from JSON:: >>> import stormpy @@ -15,14 +17,14 @@ We start by loading a simple DFT containing an AND gate from JSON:: >>> import stormpy.examples >>> import stormpy.examples.files >>> path_json = stormpy.examples.files.dft_json_and - >>> dft_small = stormpy.dft.load_dft_json(path_json) + >>> dft_small = stormpy.dft.load_dft_json_file(path_json) >>> print(dft_small) Top level index: 2, Nr BEs2 Next we load a more complex DFT from the Galileo format:: >>> path_galileo = stormpy.examples.files.dft_galileo_hecs - >>> dft = stormpy.dft.load_dft_galileo(path_galileo) + >>> dft = stormpy.dft.load_dft_galileo_file(path_galileo) After loading the DFT, we can display some common statistics about the model:: diff --git a/examples/dfts/01-dfts.py b/examples/dfts/01-dfts.py index 691d150..160c975 100644 --- a/examples/dfts/01-dfts.py +++ b/examples/dfts/01-dfts.py @@ -8,12 +8,12 @@ import stormpy.examples.files def example_dfts_01(): # Load from JSON path_json = stormpy.examples.files.dft_json_and - dft_small = stormpy.dft.load_dft_json(path_json) + dft_small = stormpy.dft.load_dft_json_file(path_json) print(dft_small) # Load from Galileo path = stormpy.examples.files.dft_galileo_hecs - dft = stormpy.dft.load_dft_galileo(path) + dft = stormpy.dft.load_dft_galileo_file(path) print("DFT with {} elements.".format(dft.nr_elements())) print("DFT has {} BEs and {} dynamic elements.".format(dft.nr_be(), dft.nr_dynamic())) diff --git a/src/dft/io.cpp b/src/dft/io.cpp index 6c56334..38526d9 100644 --- a/src/dft/io.cpp +++ b/src/dft/io.cpp @@ -6,13 +6,15 @@ void define_input(py::module& m) { // Load DFT input - m.def("load_dft_galileo", &storm::api::loadDFTGalileo, "Load DFT from Galileo file", py::arg("path")); + m.def("load_dft_galileo_file", &storm::api::loadDFTGalileoFile, "Load DFT from Galileo file", py::arg("path")); // Parse Jani model - m.def("load_dft_json", &storm::api::loadDFTJson, "Load DFT from JSON file", py::arg("path")); + m.def("load_dft_json_file", &storm::api::loadDFTJsonFile, "Load DFT from JSON file", py::arg("path")); + m.def("load_dft_json_string", &storm::api::loadDFTJsonString, "Load DFT from JSON string", py::arg("json_string")); } void define_output(py::module& m) { // Export DFT - m.def("export_dft_json", &storm::api::exportDFTToJson, "Export DFT to JSON file", py::arg("dft"), py::arg("path")); + m.def("export_dft_json_file", &storm::api::exportDFTToJsonFile, "Export DFT to JSON file", py::arg("dft"), py::arg("path")); + m.def("export_dft_json_string", &storm::api::exportDFTToJsonString, "Export DFT to JSON string", py::arg("dft")); } diff --git a/tests/dft/test_build.py b/tests/dft/test_build.py index d9a84fe..4a3ba51 100644 --- a/tests/dft/test_build.py +++ b/tests/dft/test_build.py @@ -9,21 +9,21 @@ from configurations import dft @dft class TestBuild: def test_load_dft_json(self): - dft = stormpy.dft.load_dft_json(get_example_path("dft", "and.json")) + dft = stormpy.dft.load_dft_json_file(get_example_path("dft", "and.json")) assert dft.nr_elements() == 3 assert dft.nr_be() == 2 assert dft.nr_dynamic() == 0 assert not dft.can_have_nondeterminism() def test_load_dft_galileo(self): - dft = stormpy.dft.load_dft_galileo(get_example_path("dft", "hecs.dft")) + dft = stormpy.dft.load_dft_galileo_file(get_example_path("dft", "hecs.dft")) assert dft.nr_elements() == 23 assert dft.nr_be() == 13 assert dft.nr_dynamic() == 2 assert not dft.can_have_nondeterminism() def test_analyze_mttf(self): - dft = stormpy.dft.load_dft_json(get_example_path("dft", "and.json")) + dft = stormpy.dft.load_dft_json_file(get_example_path("dft", "and.json")) formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") assert dft.nr_elements() == 3 results = stormpy.dft.analyze_dft(dft, [formulas[0].raw_formula]) From 1c4e589a11413edca343b1b6ccfd218e9b71282b Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 8 Aug 2018 15:10:31 +0200 Subject: [PATCH 089/147] I/O tests for DFTs --- tests/dft/test_analysis.py | 17 +++++++++++ tests/dft/test_build.py | 30 ------------------ tests/dft/test_io.py | 62 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 30 deletions(-) create mode 100644 tests/dft/test_analysis.py delete mode 100644 tests/dft/test_build.py create mode 100644 tests/dft/test_io.py diff --git a/tests/dft/test_analysis.py b/tests/dft/test_analysis.py new file mode 100644 index 0000000..1028676 --- /dev/null +++ b/tests/dft/test_analysis.py @@ -0,0 +1,17 @@ +import stormpy +import stormpy.logic +from helpers.helper import get_example_path + +import math +from configurations import dft + + +@dft +class TestAnalysis: + + def test_analyze_mttf(self): + dft = stormpy.dft.load_dft_json_file(get_example_path("dft", "and.json")) + formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") + assert dft.nr_elements() == 3 + results = stormpy.dft.analyze_dft(dft, [formulas[0].raw_formula]) + assert math.isclose(results[0], 3) diff --git a/tests/dft/test_build.py b/tests/dft/test_build.py deleted file mode 100644 index 4a3ba51..0000000 --- a/tests/dft/test_build.py +++ /dev/null @@ -1,30 +0,0 @@ -import stormpy -import stormpy.logic -from helpers.helper import get_example_path - -import math -from configurations import dft - - -@dft -class TestBuild: - def test_load_dft_json(self): - dft = stormpy.dft.load_dft_json_file(get_example_path("dft", "and.json")) - assert dft.nr_elements() == 3 - assert dft.nr_be() == 2 - assert dft.nr_dynamic() == 0 - assert not dft.can_have_nondeterminism() - - def test_load_dft_galileo(self): - dft = stormpy.dft.load_dft_galileo_file(get_example_path("dft", "hecs.dft")) - assert dft.nr_elements() == 23 - assert dft.nr_be() == 13 - assert dft.nr_dynamic() == 2 - assert not dft.can_have_nondeterminism() - - def test_analyze_mttf(self): - dft = stormpy.dft.load_dft_json_file(get_example_path("dft", "and.json")) - formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") - assert dft.nr_elements() == 3 - results = stormpy.dft.analyze_dft(dft, [formulas[0].raw_formula]) - assert math.isclose(results[0], 3) diff --git a/tests/dft/test_io.py b/tests/dft/test_io.py new file mode 100644 index 0000000..33a8149 --- /dev/null +++ b/tests/dft/test_io.py @@ -0,0 +1,62 @@ +import os + +import stormpy +import stormpy.logic +from helpers.helper import get_example_path + +from configurations import dft + + +@dft +class TestDftLoad: + def test_load_dft_galileo_file(self): + dft = stormpy.dft.load_dft_galileo_file(get_example_path("dft", "hecs.dft")) + assert dft.nr_elements() == 23 + assert dft.nr_be() == 13 + assert dft.nr_dynamic() == 2 + assert not dft.can_have_nondeterminism() + + def test_load_dft_json_file(self): + dft = stormpy.dft.load_dft_json_file(get_example_path("dft", "and.json")) + assert dft.nr_elements() == 3 + assert dft.nr_be() == 2 + assert dft.nr_dynamic() == 0 + assert not dft.can_have_nondeterminism() + + def test_load_dft_json_string(self): + # Build json string + json_node_a = '{"data": {"id":"0", "name":"A", "type":"be", "rate":"1", "dorm":"1", "label":"A (1)"}, "group":"nodes", "classes":"be"}' + json_node_b = '{"data": {"id":"1", "name":"B", "type":"be", "rate":"1", "dorm":"1", "label":"B (1)"}, "group":"nodes", "classes":"be"}' + json_node_c = '{"data": {"id":"6", "name":"Z", "type":"pand", "children":["0", "1"], "label":"Z"}, "group":"nodes", "classes":"pand"}' + json_string = '{"toplevel": "6", "parameters": {}, "nodes": [' + json_node_a + ',' + json_node_b + ',' + json_node_c + ']}' + # Load + dft = stormpy.dft.load_dft_json_string(json_string) + assert dft.nr_elements() == 3 + assert dft.nr_be() == 2 + assert dft.nr_dynamic() == 1 + assert not dft.can_have_nondeterminism() + + +class TestDftExport: + def test_export_dft_json_string(self): + dft = stormpy.dft.load_dft_galileo_file(get_example_path("dft", "hecs.dft")) + assert dft.nr_elements() == 23 + assert dft.nr_be() == 13 + assert dft.nr_dynamic() == 2 + json_string = stormpy.dft.export_dft_json_string(dft) + dft2 = stormpy.dft.load_dft_json_string(json_string) + assert dft2.nr_elements() == 23 + assert dft2.nr_be() == 13 + assert dft2.nr_dynamic() == 2 + + def test_export_dft_json_file(self, tmpdir): + dft = stormpy.dft.load_dft_galileo_file(get_example_path("dft", "hecs.dft")) + assert dft.nr_elements() == 23 + assert dft.nr_be() == 13 + assert dft.nr_dynamic() == 2 + export_file = os.path.join(str(tmpdir), "hecs.json") + stormpy.dft.export_dft_json_file(dft, export_file) + dft2 = stormpy.dft.load_dft_json_file(export_file) + assert dft2.nr_elements() == 23 + assert dft2.nr_be() == 13 + assert dft2.nr_dynamic() == 2 From bef0a792d6e4a2cac12d933e7543a4be169ff7c7 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 21 Aug 2018 10:13:21 +0200 Subject: [PATCH 090/147] add init for probability operator --- src/logic/formulae.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index 90dc80b..31d1e31 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -80,7 +80,9 @@ void define_formulae(py::module& m) { ; py::class_>(m, "TimeOperator", "The time operator", operatorFormula); py::class_>(m, "LongRunAvarageOperator", "Long run average operator", operatorFormula); - py::class_>(m, "ProbabilityOperator", "Probability operator", operatorFormula); + py::class_>(m, "ProbabilityOperator", "Probability operator", operatorFormula) + .def(py::init>(), "construct probability operator formula", py::arg("subformula")) + ; py::class_>(m, "RewardOperator", "Reward operator", operatorFormula) .def("has_reward_name", &storm::logic::RewardOperatorFormula::hasRewardModelName) .def_property_readonly("reward_name", &storm::logic::RewardOperatorFormula::getRewardModelName); From 4b48c4d75bc3f893c84c62a58a355784ab4292ff Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 21 Aug 2018 15:18:27 +0200 Subject: [PATCH 091/147] get manager for an expression --- src/storage/expressions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index 4132c1e..e91f051 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -46,6 +46,7 @@ void define_expressions(py::module& m) { .def("has_integer_type", &storm::expressions::Expression::hasIntegerType, "Check if the expression is an integer") .def("has_rational_type", &storm::expressions::Expression::hasRationalType, "Check if the expression is a rational") .def_property_readonly("type", &storm::expressions::Expression::getType, "Get the Type") + .def_property_readonly("manager", &storm::expressions::Expression::getManager, "Get the manager") .def("__str__", &storm::expressions::Expression::toString, "To string") .def_static("plus", [](Expression const& lhs, Expression const& rhs) {return lhs + rhs;}) From 69202f2ddff374619740d90d9af56d3778f00506 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 29 Aug 2018 17:31:40 +0200 Subject: [PATCH 092/147] Added space --- doc/source/doc/dfts.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/doc/dfts.rst b/doc/source/doc/dfts.rst index d46d821..30d894a 100644 --- a/doc/source/doc/dfts.rst +++ b/doc/source/doc/dfts.rst @@ -48,3 +48,4 @@ In our example we compute the `Mean-time-to-failure (MTTF)`:: >>> result = results[0] >>> print("MTTF: {:.2f}".format(result)) MTTF: 363.89 + From 8c8e46b8a31749837818d1d6430432b505af267f Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 29 Aug 2018 17:32:10 +0200 Subject: [PATCH 093/147] Added elimination of reward accumulations in Jani --- src/storage/jani.cpp | 74 ++++++++++++++++++++------------ tests/core/test_modelchecking.py | 11 ++--- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index 192f680..877518a 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "src/helpers.h" using namespace storm::jani; @@ -15,53 +16,70 @@ std::string janiToString(Model const& m) { void define_jani(py::module& m) { py::class_> md(m, "JaniModel", "A Jani Model"); md.def_property_readonly("name", &Model::getName, "model name") - .def_property_readonly("model_type", &storm::jani::Model::getModelType, "Model type") - .def_property_readonly("automata", [](const Model& model) {return model.getAutomata();}, "get automata") - .def_property_readonly("constants", [](const Model& model) {return model.getConstants();}, "get constants") - .def("restrict_edges", &Model::restrictEdges, "restrict model to edges given by set", py::arg("edge_set")) - .def_property_readonly("expression_manager", &Model::getExpressionManager, "get expression manager", pybind11::return_value_policy::reference_internal) - .def_property_readonly("has_undefined_constants", &Model::hasUndefinedConstants, "Flag if program has undefined constants") - .def_property_readonly("undefined_constants_are_graph_preserving", &Model::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") - .def("__str__", &janiToString) - .def("define_constants", &Model::defineUndefinedConstants, "define constants with a mapping from the corresponding expression variables to expressions", py::arg("map")) - .def("substitute_constants", &Model::substituteConstants, "substitute constants") - .def("get_automaton_index", &Model::getAutomatonIndex, "get index for automaton name") - .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") - .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") - ; + .def_property_readonly("model_type", &storm::jani::Model::getModelType, "Model type") + .def_property_readonly("automata", [](const Model& model) {return model.getAutomata();}, "get automata") + .def_property_readonly("constants", [](const Model& model) {return model.getConstants();}, "get constants") + .def("restrict_edges", &Model::restrictEdges, "restrict model to edges given by set", py::arg("edge_set")) + .def_property_readonly("expression_manager", &Model::getExpressionManager, "get expression manager", pybind11::return_value_policy::reference_internal) + .def_property_readonly("has_undefined_constants", &Model::hasUndefinedConstants, "Flag if program has undefined constants") + .def_property_readonly("undefined_constants_are_graph_preserving", &Model::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") + .def("__str__", &janiToString) + .def("define_constants", &Model::defineUndefinedConstants, "define constants with a mapping from the corresponding expression variables to expressions", py::arg("map")) + .def("substitute_constants", &Model::substituteConstants, "substitute constants") + .def("get_automaton_index", &Model::getAutomatonIndex, "get index for automaton name") + .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") + .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") + ; + py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); automaton.def_property_readonly("edges",[](const Automaton& a) {return a.getEdges();}, "get edges") - .def_property_readonly("name", &Automaton::getName); + .def_property_readonly("name", &Automaton::getName) + ; + py::class_> edge(m, "JaniEdge", "A Jani Edge"); edge.def_property_readonly("source_location_index", &Edge::getSourceLocationIndex, "index for source location") - .def_property_readonly("destinations", &Edge::getDestinations, "edge destinations") - .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") - .def_property_readonly("guard", &Edge::getGuard, "edge guard") - .def("substitute", &Edge::substitute, py::arg("mapping")) - .def("has_silent_action", &Edge::hasSilentAction, "Is the edge labelled with the silent action"); + .def_property_readonly("destinations", &Edge::getDestinations, "edge destinations") + .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") + .def_property_readonly("guard", &Edge::getGuard, "edge guard") + .def("substitute", &Edge::substitute, py::arg("mapping")) + .def("has_silent_action", &Edge::hasSilentAction, "Is the edge labelled with the silent action") + ; + py::class_> templateEdge(m, "JaniTemplateEdge", "Template edge, internal data structure for edges"); templateEdge.def(py::init()); py::class_> edgeDestination(m, "JaniEdgeDestination", "Destination in Jani"); edgeDestination.def_property_readonly("target_location_index", &EdgeDestination::getLocationIndex) - .def_property_readonly("probability", &EdgeDestination::getProbability) - .def_property_readonly("assignments", &EdgeDestination::getOrderedAssignments) + .def_property_readonly("probability", &EdgeDestination::getProbability) + .def_property_readonly("assignments", &EdgeDestination::getOrderedAssignments) ; + py::class_> orderedAssignments(m, "JaniOrderedAssignments", "Set of assignments"); orderedAssignments.def("__iter__", [](OrderedAssignments &v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ - .def("__str__", &streamToString) - ; + .def("__str__", &streamToString) + ; + py::class_> assignment(m, "JaniAssignment", "Jani Assignment"); assignment.def("__str__", &streamToString) - .def_property("expression", &Assignment::getAssignedExpression, &Assignment::setAssignedExpression) + .def_property("expression", &Assignment::getAssignedExpression, &Assignment::setAssignedExpression) ; + py::class_> constant(m, "JaniConstant", "A Constant in JANI"); constant.def_property_readonly("defined", &Constant::isDefined, "is constant defined by some expression") - .def_property_readonly("name", &Constant::getName, "name of constant") - .def_property_readonly("type", &Constant::getType, "type of constant") - .def_property_readonly("expression_variable", &Constant::getExpressionVariable, "expression variable for this constant"); + .def_property_readonly("name", &Constant::getName, "name of constant") + .def_property_readonly("type", &Constant::getType, "type of constant") + .def_property_readonly("expression_variable", &Constant::getExpressionVariable, "expression variable for this constant") + ; + + + m.def("eliminate_reward_accumulations", [](const Model& model, std::vector& properties) { + storm::logic::RewardAccumulationEliminationVisitor v(model); + v.eliminateRewardAccumulations(properties); + return properties; + }, "Eliminate reward accumulations", py::arg("model"), py::arg("properties")); + } \ No newline at end of file diff --git a/tests/core/test_modelchecking.py b/tests/core/test_modelchecking.py index a750c74..1d4b15f 100644 --- a/tests/core/test_modelchecking.py +++ b/tests/core/test_modelchecking.py @@ -32,17 +32,18 @@ class TestModelChecking: def test_model_checking_jani_dtmc(self): jani_model, properties = stormpy.parse_jani_model(get_example_path("dtmc", "die.jani")) - formula = properties["Probability to throw a six"] - formula2 = properties["Expected number of coin flips"] - model = stormpy.build_model(jani_model, [formula, formula2]) + formulas = [properties["Probability to throw a six"], properties["Expected number of coin flips"]] + formulas = stormpy.eliminate_reward_accumulations(jani_model, formulas) + assert len(formulas) == 2 + model = stormpy.build_model(jani_model, formulas) assert model.nr_states == 13 assert model.nr_transitions == 20 assert len(model.initial_states) == 1 initial_state = model.initial_states[0] assert initial_state == 0 - result = stormpy.model_checking(model, formula) + result = stormpy.model_checking(model, formulas[0]) assert math.isclose(result.at(initial_state), 1 / 6) - result = stormpy.model_checking(model, formula2) + result = stormpy.model_checking(model, formulas[1]) assert math.isclose(result.at(initial_state), 11 / 3) def test_model_checking_dtmc_all_labels(self): From 63d3e96283b6900922fd683ea637ece0c3aba4b8 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 14 Sep 2018 17:47:22 +0200 Subject: [PATCH 094/147] Updated JaniPropery bindings according to changes in Storm --- src/core/input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/input.cpp b/src/core/input.cpp index 23d696c..1df9f9c 100644 --- a/src/core/input.cpp +++ b/src/core/input.cpp @@ -5,7 +5,7 @@ void define_property(py::module& m) { py::class_(m, "Property", "Property") - .def(py::init const&, std::string const&>(), "Construct property from formula", py::arg("name"), py::arg("formula"), py::arg("comment") = "") + .def(py::init const&, std::set const&, std::string const&>(), "Construct property from formula", py::arg("name"), py::arg("formula"), py::arg("undefined_constants") = std::set(), py::arg("comment") = "") .def(py::init()) .def_property_readonly("name", &storm::jani::Property::getName, "Obtain the name of the property") .def_property_readonly("raw_formula", &storm::jani::Property::getRawFormula, "Obtain the formula directly") From 0ef93e57b31e8e0b4ff1eadfb77a508925ec1c14 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 28 Sep 2018 13:25:42 +0200 Subject: [PATCH 095/147] Adapted Jani bindings according to changes in Storm --- src/core/input.cpp | 6 ++++-- src/storage/jani.cpp | 10 +++++++--- src/storage/prism.cpp | 5 ++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/input.cpp b/src/core/input.cpp index 1df9f9c..3debf33 100644 --- a/src/core/input.cpp +++ b/src/core/input.cpp @@ -18,9 +18,11 @@ void define_property(py::module& m) { void define_input(py::module& m) { // Parse Prism program - m.def("parse_prism_program", &storm::api::parseProgram, "Parse Prism program", py::arg("path"), py::arg("prism_compat") = false); + m.def("parse_prism_program", &storm::api::parseProgram, "Parse Prism program", py::arg("path"), py::arg("prism_compat") = false, py::arg("simplify") = true); // Parse Jani model - m.def("parse_jani_model", &storm::api::parseJaniModel, "Parse Jani model", py::arg("path")); + m.def("parse_jani_model", [](std::string const& path){ + return storm::api::parseJaniModel(path); + }, "Parse Jani model", py::arg("path")); // JaniType py::enum_(m, "JaniModelType", "Type of the Jani model") diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index 877518a..db5a2bc 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -32,13 +32,17 @@ void define_jani(py::module& m) { ; py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); - automaton.def_property_readonly("edges",[](const Automaton& a) {return a.getEdges();}, "get edges") - .def_property_readonly("name", &Automaton::getName) + automaton.def_property_readonly("edges",[](const Automaton& a) { + return a.getEdges(); + }, "get edges") + .def_property_readonly("name", &Automaton::getName) ; py::class_> edge(m, "JaniEdge", "A Jani Edge"); edge.def_property_readonly("source_location_index", &Edge::getSourceLocationIndex, "index for source location") - .def_property_readonly("destinations", &Edge::getDestinations, "edge destinations") + .def_property_readonly("destinations", [](Edge const& e) { + return e.getDestinations(); + }, "edge destinations") .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") .def_property_readonly("guard", &Edge::getGuard, "edge guard") .def("substitute", &Edge::substitute, py::arg("mapping")) diff --git a/src/storage/prism.cpp b/src/storage/prism.cpp index b1b8a7c..57b0b80 100644 --- a/src/storage/prism.cpp +++ b/src/storage/prism.cpp @@ -3,6 +3,7 @@ #include "src/helpers.h" #include #include +#include using namespace storm::prism; @@ -20,7 +21,9 @@ void define_prism(py::module& m) { .def("simplify", &Program::simplify, "Simplify") .def("used_constants",&Program::usedConstants, "Compute Used Constants") .def_property_readonly("expression_manager", &Program::getManager, "Get the expression manager for expressions in this program") - .def("to_jani", &Program::toJaniWithLabelRenaming, "Transform to Jani program", py::arg("all_variables_global")=false, py::arg("suffix") = "", py::arg("standard_compliant")=false) + .def("to_jani", [](storm::prism::Program const& program, std::vector const& properties, bool allVariablesGlobal, std::string suffix) { + return program.toJani(properties, allVariablesGlobal, suffix); + }, "Transform to Jani program", py::arg("properties"), py::arg("all_variables_global") = true, py::arg("suffix") = "") .def("__str__", &streamToString); py::class_ module(m, "PrismModule", "A module in a Prism program"); From b2b647203b3d3847325607fa135fb74b04c88ff3 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 2 Oct 2018 18:51:37 +0200 Subject: [PATCH 096/147] add pomdp support to stormpy --- examples/reward_models.py | 0 lib/stormpy/__init__.py | 2 + lib/stormpy/examples/files/pomdp/maze_2.prism | 139 ++++++++++++++++++ src/storage/model.cpp | 11 ++ tests/storage/test_model.py | 8 + 5 files changed, 160 insertions(+) create mode 100644 examples/reward_models.py create mode 100644 lib/stormpy/examples/files/pomdp/maze_2.prism diff --git a/examples/reward_models.py b/examples/reward_models.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 9d3829c..72d0e3e 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -48,6 +48,8 @@ def _convert_sparse_model(model, parametric=False): return model._as_sparse_dtmc() elif model.model_type == ModelType.MDP: return model._as_sparse_mdp() + elif model.model_type == ModelType.POMDP: + return model._as_sparse_pomdp() elif model.model_type == ModelType.CTMC: return model._as_sparse_ctmc() elif model.model_type == ModelType.MA: diff --git a/lib/stormpy/examples/files/pomdp/maze_2.prism b/lib/stormpy/examples/files/pomdp/maze_2.prism new file mode 100644 index 0000000..f89d179 --- /dev/null +++ b/lib/stormpy/examples/files/pomdp/maze_2.prism @@ -0,0 +1,139 @@ + + +// maze example (POMDP) +// slightly extends that presented in +// Littman, Cassandra and Kaelbling +// Learning policies for partially observable environments: Scaling up +// Technical Report CS, Brown University +// gxn 29/01/16 + +// state space (value of variable "s") + +// 0 1 2 3 4 +// 5 6 7 +// 8 9 10 +// 11 13 12 + +// 13 is the target + +pomdp + +// can observe the walls and target +observables + o +endobservables +// o=0 - observation in initial state +// o=1 - west and north walls (s0) +// o=2 - north and south ways (s1 and s3) +// o=3 - north wall (s2) +// o=4 - east and north way (s4) +// o=5 - east and west walls (s5, s6, s7, s8, s9 and s10) +// o=6 - east, west and south walls (s11 and s12) +// o=7 - the target (s13) + +module maze + + s : [-1..13]; + o : [0..7]; + + // initialisation + [] s=-1 -> 1/13 : (s'=0) & (o'=1) + + 1/13 : (s'=1) & (o'=2) + + 1/13 : (s'=2) & (o'=3) + + 1/13 : (s'=3) & (o'=2) + + 1/13 : (s'=4) & (o'=4) + + 1/13 : (s'=5) & (o'=5) + + 1/13 : (s'=6) & (o'=5) + + 1/13 : (s'=7) & (o'=5) + + 1/13 : (s'=8) & (o'=5) + + 1/13 : (s'=9) & (o'=5) + + 1/13 : (s'=10) & (o'=5) + + 1/13 : (s'=11) & (o'=6) + + 1/13 : (s'=12) & (o'=6); + + // moving around the maze + + [east] s=0 -> (s'=1) & (o'=2); + [west] s=0 -> (s'=0); + [north] s=0 -> (s'=0); + [south] s=0 -> (s'=5) & (o'=5); + + [east] s=1 -> (s'=2) & (o'=3); + [west] s=1 -> (s'=0) & (o'=1); + [north] s=1 -> (s'=1); + [south] s=1 -> (s'=1); + + [east] s=2 -> (s'=3) & (o'=2); + [west] s=2 -> (s'=1) & (o'=2); + [north] s=2 -> (s'=2); + [south] s=2 -> (s'=6) & (o'=5); + + [east] s=3 -> (s'=4) & (o'=4); + [west] s=3 -> (s'=2) & (o'=2); + [north] s=3 -> (s'=3); + [south] s=3 -> (s'=3); + + [east] s=4 -> (s'=4); + [west] s=4 -> (s'=3) & (o'=2); + [north] s=4 -> (s'=4); + [south] s=4 -> (s'=7) & (o'=5); + + [east] s=5 -> (s'=5); + [west] s=5 -> (s'=5); + [north] s=5 -> (s'=0) & (o'=1); + [south] s=5 -> (s'=8); + + [east] s=6 -> (s'=6); + [west] s=6 -> (s'=6); + [north] s=6 -> (s'=2) & (o'=3); + [south] s=6 -> (s'=9); + + [east] s=7 -> (s'=7); + [west] s=7 -> (s'=7); + [north] s=7 -> (s'=4) & (o'=4); + [south] s=7 -> (s'=10); + + [east] s=8 -> (s'=8); + [west] s=8 -> (s'=8); + [north] s=8 -> (s'=5); + [south] s=8 -> (s'=11) & (o'=6); + + [east] s=9 -> (s'=9); + [west] s=9 -> (s'=9); + [north] s=9 -> (s'=6); + [south] s=9 -> (s'=13) & (o'=7); + + [east] s=10 -> (s'=9); + [west] s=10 -> (s'=9); + [north] s=10 -> (s'=7); + [south] s=10 -> (s'=12) & (o'=6); + + [east] s=11 -> (s'=11); + [west] s=11 -> (s'=11); + [north] s=11 -> (s'=8) & (o'=5); + [south] s=11 -> (s'=11); + + [east] s=12 -> (s'=12); + [west] s=12 -> (s'=12); + [north] s=12 -> (s'=10) & (o'=5); + [south] s=12 -> (s'=12); + + // loop when we reach the target + [done] s=13 -> true; + +endmodule + +// reward structure (number of steps to reach the target) +rewards + + [east] true : 1; + [west] true : 1; + [north] true : 1; + [south] true : 1; + +endrewards + +// target observation +label "goal" = o=7; + + diff --git a/src/storage/model.cpp b/src/storage/model.cpp index 7844dce..a9c966a 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -5,6 +5,7 @@ #include "storm/models/sparse/Model.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/sparse/Mdp.h" +#include "storm/models/sparse/Pomdp.h" #include "storm/models/sparse/Ctmc.h" #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -26,6 +27,7 @@ using ModelBase = storm::models::ModelBase; template using SparseModel = storm::models::sparse::Model; template using SparseDtmc = storm::models::sparse::Dtmc; template using SparseMdp = storm::models::sparse::Mdp; +template using SparsePomdp = storm::models::sparse::Pomdp; template using SparseCtmc = storm::models::sparse::Ctmc; template using SparseMarkovAutomaton = storm::models::sparse::MarkovAutomaton; template using SparseRewardModel = storm::models::sparse::StandardRewardModel; @@ -94,6 +96,7 @@ void define_model(py::module& m) { py::enum_(m, "ModelType", "Type of the model") .value("DTMC", storm::models::ModelType::Dtmc) .value("MDP", storm::models::ModelType::Mdp) + .value("POMDP", storm::models::ModelType::Pomdp) .value("CTMC", storm::models::ModelType::Ctmc) .value("MA", storm::models::ModelType::MarkovAutomaton) ; @@ -118,6 +121,9 @@ void define_model(py::module& m) { .def("_as_sparse_pmdp", [](ModelBase &modelbase) { return modelbase.as>(); }, "Get model as sparse pMDP") + .def("_as_sparse_pomdp", [](ModelBase &modelbase) { + return modelbase.as>(); + }, "Get model as sparse POMDP") .def("_as_sparse_ctmc", [](ModelBase &modelbase) { return modelbase.as>(); }, "Get model as sparse CTMC") @@ -184,6 +190,11 @@ void define_sparse_model(py::module& m) { py::class_, std::shared_ptr>>(m, "SparseMdp", "MDP in sparse representation", model) .def("__str__", getModelInfoPrinter("MDP")) ; + py::class_, std::shared_ptr>>(m, "SparsePomdp", "POMDP in sparse representation", model) + .def("__str__", getModelInfoPrinter("POMDP")) + .def_property_readonly("observations", &SparsePomdp::getObservations) + .def_property_readonly("nr_observations", &SparsePomdp::getNrObservations) + ; py::class_, std::shared_ptr>>(m, "SparseCtmc", "CTMC in sparse representation", model) .def("__str__", getModelInfoPrinter("CTMC")) ; diff --git a/tests/storage/test_model.py b/tests/storage/test_model.py index 2598e3b..532f88c 100644 --- a/tests/storage/test_model.py +++ b/tests/storage/test_model.py @@ -106,6 +106,14 @@ class TestSparseModel: assert not model.supports_parameters assert type(model) is stormpy.SparseCtmc + def test_build_pomdp(self): + program = stormpy.parse_prism_program(get_example_path("pomdp", "maze_2.prism")) + formulas = stormpy.parse_properties_for_prism_program("P=? [F \"goal\"]", program) + model = stormpy.build_model(program, formulas) + assert model.nr_states == 16 + assert model.nr_observations == 8 + + def test_build_ma(self): program = stormpy.parse_prism_program(get_example_path("ma", "simple.ma")) formulas = stormpy.parse_properties_for_prism_program("P=? [ F<=2 s=2 ]", program) From 9970aa391f05bdd4a191bdae5620262c4a3663ad Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 2 Oct 2018 18:52:14 +0200 Subject: [PATCH 097/147] toposort bindings --- lib/stormpy/__init__.py | 15 +++++++++++++++ src/storage/matrix.cpp | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 72d0e3e..fa6fabe 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -397,3 +397,18 @@ def compute_prob01max_states(model, phi_states, psi_states): return core._compute_prob01states_max_rationalfunc(model, phi_states, psi_states) else: return core._compute_prob01states_max_double(model, phi_states, psi_states) + +def topological_sort(model, forward=True, initial=[]): + """ + + :param model: + :param forward: + :return: + """ + matrix = model.transition_matrix if forward else model.backward_transition_matrix + if isinstance(model, storage._SparseParametricModel): + return storage._topological_sort_rf(matrix, initial) + elif isinstance(model, storage._SparseModel): + return storage._topological_sort_double(matrix, initial) + else: + raise StormError("Unknown kind of model.") \ No newline at end of file diff --git a/src/storage/matrix.cpp b/src/storage/matrix.cpp index 19b74d2..a0d87b3 100644 --- a/src/storage/matrix.cpp +++ b/src/storage/matrix.cpp @@ -1,6 +1,8 @@ #include "matrix.h" #include "storm/storage/SparseMatrix.h" #include "storm/storage/BitVector.h" + +#include "storm/utility/graph.h" #include "src/helpers.h" template using SparseMatrix = storm::storage::SparseMatrix; @@ -81,6 +83,10 @@ void define_sparse_matrix(py::module& m) { }, py::return_value_policy::reference, py::keep_alive<1, 0>()) ; + m.def("_topological_sort_double", [](SparseMatrix& matrix, std::vector initial) { return storm::utility::graph::getTopologicalSort(matrix, initial); }, "matrix"_a, "initial"_a, "get topological sort w.r.t. a transition matrix"); + m.def("_topological_sort_rf", [](SparseMatrix& matrix, std::vector initial) { return storm::utility::graph::getTopologicalSort(matrix, initial); }, "matrix"_a, "initial"_a, "get topological sort w.r.t. a transition matrix"); + + py::class_>(m, "ParametricSparseMatrix", "Parametric sparse matrix") .def("__iter__", [](SparseMatrix& matrix) { return py::make_iterator(matrix.begin(), matrix.end()); From 3648aa6f7657c654cd06dd451a4de86be789269d Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 2 Oct 2018 18:52:45 +0200 Subject: [PATCH 098/147] model to dot string --- src/storage/model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/model.cpp b/src/storage/model.cpp index a9c966a..45594d6 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -230,6 +230,7 @@ void define_sparse_model(py::module& m) { .def_property_readonly("backward_transition_matrix", &SparseModel::getBackwardTransitions, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") .def("reduce_to_state_based_rewards", &SparseModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter("ParametricModel")) + .def("to_dot", [](SparseModel& model) { std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }, "Write dot to a string") ; py::class_, std::shared_ptr>>(m, "SparseParametricDtmc", "pDTMC in sparse representation", modelRatFunc) From fdf8d8b86b550e9448cbb151dea9b8ab4e850bb8 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 3 Oct 2018 13:21:40 +0200 Subject: [PATCH 099/147] several functions to build automata --- src/storage/jani.cpp | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index db5a2bc..ecc4b93 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -26,16 +26,25 @@ void define_jani(py::module& m) { .def("__str__", &janiToString) .def("define_constants", &Model::defineUndefinedConstants, "define constants with a mapping from the corresponding expression variables to expressions", py::arg("map")) .def("substitute_constants", &Model::substituteConstants, "substitute constants") - .def("get_automaton_index", &Model::getAutomatonIndex, "get index for automaton name") + .def("get_automaton_index", &Model::getAutomatonIndex, "name"_a, "get index for automaton name") + .def("replace_automaton", &Model::replaceAutomaton, "index"_a, "new_automaton"_a, "replace automaton at index") .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") ; py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); - automaton.def_property_readonly("edges",[](const Automaton& a) { + automaton.def(py::init()) + .def_property_readonly("edges",[](const Automaton& a) { return a.getEdges(); }, "get edges") .def_property_readonly("name", &Automaton::getName) + .def_property_readonly("location_variable", &Automaton::getLocationExpressionVariable) + .def_property_readonly("variables", [](Automaton& aut) {return aut.getVariables();}) + .def_property_readonly("locations", [](Automaton& aut) {return aut.getLocations();}) + .def("add_location", &Automaton::addLocation, "location"_a, "adds a new location, returns the index") + .def("add_initial_location", [](Automaton& aut, uint64_t index) { aut.addInitialLocation(index); }, "index"_a) + .def_property_readonly("initial_location_indices", &Automaton::getInitialLocationIndices) + ; py::class_> edge(m, "JaniEdge", "A Jani Edge"); @@ -43,6 +52,7 @@ void define_jani(py::module& m) { .def_property_readonly("destinations", [](Edge const& e) { return e.getDestinations(); }, "edge destinations") + .def_property_readonly("template_edge", &Edge::getTemplateEdge, "template edge") .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") .def_property_readonly("guard", &Edge::getGuard, "edge guard") .def("substitute", &Edge::substitute, py::arg("mapping")) @@ -50,7 +60,11 @@ void define_jani(py::module& m) { ; py::class_> templateEdge(m, "JaniTemplateEdge", "Template edge, internal data structure for edges"); - templateEdge.def(py::init()); + templateEdge.def(py::init()) + .def_property_readonly("assignments", [](TemplateEdge& te) { return te.getAssignments(); }) + .def_property("guard", &TemplateEdge::getGuard, &TemplateEdge::setGuard) + .def_property_readonly("destinations",[](TemplateEdge& te) { return te.getDestinations(); }) + ; py::class_> edgeDestination(m, "JaniEdgeDestination", "Destination in Jani"); edgeDestination.def_property_readonly("target_location_index", &EdgeDestination::getLocationIndex) @@ -70,6 +84,24 @@ void define_jani(py::module& m) { .def_property("expression", &Assignment::getAssignedExpression, &Assignment::setAssignedExpression) ; + py::class_> location(m, "JaniLocation", "A Location in JANI"); + location.def_property_readonly("name", &Location::getName, "name of the location") + .def_property_readonly("assignments", [](Location& loc) {loc.getAssignments();}, "location assignments") + ; + + py::class_> variableSet(m, "JaniVariableSet", "Jani Set of Variables"); + variableSet.def(py::init<>()) + .def("__iter__", [](VariableSet &v) { + return py::make_iterator(v.begin(), v.end()); + }, py::keep_alive<0, 1>()) + .def("add_variable", [](VariableSet& vs, Variable& v) { vs.addVariable(v);} ) + ; + + py::class_> variable(m, "JaniVariable", "A Variable in JANI"); + variable.def_property_readonly("name", &Variable::getName, "name of constant") + .def_property_readonly("expression_variable", &Variable::getExpressionVariable, "expression variable for this variable") + ; + py::class_> constant(m, "JaniConstant", "A Constant in JANI"); constant.def_property_readonly("defined", &Constant::isDefined, "is constant defined by some expression") .def_property_readonly("name", &Constant::getName, "name of constant") From 350f5a09e164bd908a9cf01988becef8c9342945 Mon Sep 17 00:00:00 2001 From: sjunges Date: Thu, 4 Oct 2018 00:25:52 +0200 Subject: [PATCH 100/147] several extensions to jani datastructures --- src/storage/jani.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index ecc4b93..10d4d29 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -44,15 +44,21 @@ void define_jani(py::module& m) { .def("add_location", &Automaton::addLocation, "location"_a, "adds a new location, returns the index") .def("add_initial_location", [](Automaton& aut, uint64_t index) { aut.addInitialLocation(index); }, "index"_a) .def_property_readonly("initial_location_indices", &Automaton::getInitialLocationIndices) + .def_property("initial_states_restriction", [](Automaton& aut) { aut.getInitialStatesExpression(); }, &Automaton::setInitialStatesRestriction, "initial state restriction") + .def("add_edge", &Automaton::addEdge, "edge"_a) ; py::class_> edge(m, "JaniEdge", "A Jani Edge"); - edge.def_property_readonly("source_location_index", &Edge::getSourceLocationIndex, "index for source location") + edge.def(py::init, std::shared_ptr, std::vector>>(), + "source_location_index"_a, "action_index"_a, "rate"_a, "template_edge"_a, "destinations_with_probabilities"_a) + .def_property_readonly("source_location_index", &Edge::getSourceLocationIndex, "index for source location") .def_property_readonly("destinations", [](Edge const& e) { return e.getDestinations(); }, "edge destinations") + .def_property_readonly("action_index", &Edge::getActionIndex, "action index") .def_property_readonly("template_edge", &Edge::getTemplateEdge, "template edge") + .def_property_readonly("rate", &Edge::getOptionalRate, "edge rate") .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") .def_property_readonly("guard", &Edge::getGuard, "edge guard") .def("substitute", &Edge::substitute, py::arg("mapping")) @@ -64,6 +70,7 @@ void define_jani(py::module& m) { .def_property_readonly("assignments", [](TemplateEdge& te) { return te.getAssignments(); }) .def_property("guard", &TemplateEdge::getGuard, &TemplateEdge::setGuard) .def_property_readonly("destinations",[](TemplateEdge& te) { return te.getDestinations(); }) + .def("add_destination", &TemplateEdge::addDestination) ; py::class_> edgeDestination(m, "JaniEdgeDestination", "Destination in Jani"); @@ -72,11 +79,17 @@ void define_jani(py::module& m) { .def_property_readonly("assignments", &EdgeDestination::getOrderedAssignments) ; + py::class_> templateEdgeDestination(m, "JaniTemplateEdgeDestination", "Template edge destination, internal data structure for edge destinations"); + templateEdgeDestination.def(py::init(), "ordered_assignments"_a) + .def_property_readonly("assignments", [](TemplateEdgeDestination& ted) {return ted.getOrderedAssignments();}); + py::class_> orderedAssignments(m, "JaniOrderedAssignments", "Set of assignments"); orderedAssignments.def("__iter__", [](OrderedAssignments &v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ .def("__str__", &streamToString) + .def("clone", &OrderedAssignments::clone, "clone assignments (performs a deep copy)") + .def("add", [](OrderedAssignments& oa, Assignment const& newAssignment, bool addToExisting) {return oa.add(newAssignment, addToExisting); }, "new_assignment"_a, "add_to_existing"_a = false) ; py::class_> assignment(m, "JaniAssignment", "Jani Assignment"); @@ -102,6 +115,10 @@ void define_jani(py::module& m) { .def_property_readonly("expression_variable", &Variable::getExpressionVariable, "expression variable for this variable") ; + py::class_ bivariable(m, "JaniBoundedIntegerVariable", "A Bounded Integer", variable); + bivariable.def(py::init(), + "name"_a, "expression_variable"_a, "init_value"_a, "lower_bound"_a, "upper_bound"_a); + py::class_> constant(m, "JaniConstant", "A Constant in JANI"); constant.def_property_readonly("defined", &Constant::isDefined, "is constant defined by some expression") .def_property_readonly("name", &Constant::getName, "name of constant") From 0e7a194f5bab03564338bb0bc605ff569bad4d81 Mon Sep 17 00:00:00 2001 From: sjunges Date: Thu, 4 Oct 2018 22:39:27 +0200 Subject: [PATCH 101/147] support for chaning the build_temp folder --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 9475487..48068e8 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ class CMakeBuild(build_ext): ('disable-dft', None, 'Disable support for DFTs'), ('disable-pars', None, 'Disable support for parametric models'), ('debug', None, 'Build in Debug mode'), - ('jobs=', 'j', 'Number of jobs to use for compiling'), + ('jobs=', 'j', 'Number of jobs to use for compiling') ] config = SetupConfig() @@ -50,12 +50,13 @@ class CMakeBuild(build_ext): ", ".join(e.name for e in self.extensions)) # Build cmake version info + print("Stormpy - Building into {}".format(self.build_temp)) build_temp_version = self.build_temp + "-version" setup_helper.ensure_dir_exists(build_temp_version) # Write config - setup_helper.ensure_dir_exists("build") - self.config.write_config("build/build_config.cfg") + setup_helper.ensure_dir_exists(self.build_temp) + self.config.write_config(os.path.join(self.build_temp, "build_config.cfg")) cmake_args = [] storm_dir = os.path.expanduser(self.config.get_as_string("storm_dir")) From 6a79cfdfa99497d690e56fffb3f22dcedbc1deca Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 4 Oct 2018 22:51:23 +0200 Subject: [PATCH 102/147] several further jani code wrapped, including a fix for actually returning automaton variables by reference --- src/storage/expressions.cpp | 2 ++ src/storage/jani.cpp | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index e91f051..f43f8ae 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -8,6 +8,7 @@ //Define python bindings void define_expressions(py::module& m) { using Expression = storm::expressions::Expression; + using Variable = storm::expressions::Variable; @@ -47,6 +48,7 @@ void define_expressions(py::module& m) { .def("has_rational_type", &storm::expressions::Expression::hasRationalType, "Check if the expression is a rational") .def_property_readonly("type", &storm::expressions::Expression::getType, "Get the Type") .def_property_readonly("manager", &storm::expressions::Expression::getManager, "Get the manager") + .def("substitute", [](Expression const& expr, std::map const& map) { return expr.substitute(map);}, "substitution_map"_a) .def("__str__", &storm::expressions::Expression::toString, "To string") .def_static("plus", [](Expression const& lhs, Expression const& rhs) {return lhs + rhs;}) diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index 10d4d29..d18f766 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -14,11 +14,13 @@ std::string janiToString(Model const& m) { } void define_jani(py::module& m) { + py::class_> md(m, "JaniModel", "A Jani Model"); md.def_property_readonly("name", &Model::getName, "model name") .def_property_readonly("model_type", &storm::jani::Model::getModelType, "Model type") - .def_property_readonly("automata", [](const Model& model) {return model.getAutomata();}, "get automata") - .def_property_readonly("constants", [](const Model& model) {return model.getConstants();}, "get constants") + .def("set_model_type", &Model::setModelType, "Sets (only) the model type") + .def_property_readonly("automata", [](const Model& model) -> auto& {return model.getAutomata();}, "get automata") + .def_property_readonly("constants", [](const Model& model) -> auto& {return model.getConstants();}, "get constants") .def("restrict_edges", &Model::restrictEdges, "restrict model to edges given by set", py::arg("edge_set")) .def_property_readonly("expression_manager", &Model::getExpressionManager, "get expression manager", pybind11::return_value_policy::reference_internal) .def_property_readonly("has_undefined_constants", &Model::hasUndefinedConstants, "Flag if program has undefined constants") @@ -26,10 +28,12 @@ void define_jani(py::module& m) { .def("__str__", &janiToString) .def("define_constants", &Model::defineUndefinedConstants, "define constants with a mapping from the corresponding expression variables to expressions", py::arg("map")) .def("substitute_constants", &Model::substituteConstants, "substitute constants") + .def("remove_constant", &Model::removeConstant, "remove a constant. Make sure the constant does not appear in the model.", "constant_name"_a) .def("get_automaton_index", &Model::getAutomatonIndex, "name"_a, "get index for automaton name") .def("replace_automaton", &Model::replaceAutomaton, "index"_a, "new_automaton"_a, "replace automaton at index") .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") + .def("finalize", &Model::finalize,"finalizes the model. After this action, be careful changing the data structure.") ; py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); @@ -39,8 +43,8 @@ void define_jani(py::module& m) { }, "get edges") .def_property_readonly("name", &Automaton::getName) .def_property_readonly("location_variable", &Automaton::getLocationExpressionVariable) - .def_property_readonly("variables", [](Automaton& aut) {return aut.getVariables();}) - .def_property_readonly("locations", [](Automaton& aut) {return aut.getLocations();}) + .def_property_readonly("variables", [](Automaton& aut) -> VariableSet& {return aut.getVariables();}, py::return_value_policy::reference_internal) + .def_property_readonly("locations", [](Automaton& aut) -> auto& {return aut.getLocations();}) .def("add_location", &Automaton::addLocation, "location"_a, "adds a new location, returns the index") .def("add_initial_location", [](Automaton& aut, uint64_t index) { aut.addInitialLocation(index); }, "index"_a) .def_property_readonly("initial_location_indices", &Automaton::getInitialLocationIndices) @@ -107,7 +111,9 @@ void define_jani(py::module& m) { .def("__iter__", [](VariableSet &v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>()) - .def("add_variable", [](VariableSet& vs, Variable& v) { vs.addVariable(v);} ) + .def("add_variable", [](VariableSet& vs, Variable& v) { vs.addVariable(v); }) + .def("add_bounded_integer_variable", [](VariableSet& vs, BoundedIntegerVariable& v) { return vs.addVariable(v);}, "variable"_a) + .def("empty", &VariableSet::empty, "is there a variable in the set?") ; py::class_> variable(m, "JaniVariable", "A Variable in JANI"); From d3b303872cb0a65cb10f3078d366e8809907ae0e Mon Sep 17 00:00:00 2001 From: sjunges Date: Thu, 4 Oct 2018 22:39:27 +0200 Subject: [PATCH 103/147] support for chaning the build_temp folder --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 9475487..48068e8 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ class CMakeBuild(build_ext): ('disable-dft', None, 'Disable support for DFTs'), ('disable-pars', None, 'Disable support for parametric models'), ('debug', None, 'Build in Debug mode'), - ('jobs=', 'j', 'Number of jobs to use for compiling'), + ('jobs=', 'j', 'Number of jobs to use for compiling') ] config = SetupConfig() @@ -50,12 +50,13 @@ class CMakeBuild(build_ext): ", ".join(e.name for e in self.extensions)) # Build cmake version info + print("Stormpy - Building into {}".format(self.build_temp)) build_temp_version = self.build_temp + "-version" setup_helper.ensure_dir_exists(build_temp_version) # Write config - setup_helper.ensure_dir_exists("build") - self.config.write_config("build/build_config.cfg") + setup_helper.ensure_dir_exists(self.build_temp) + self.config.write_config(os.path.join(self.build_temp, "build_config.cfg")) cmake_args = [] storm_dir = os.path.expanduser(self.config.get_as_string("storm_dir")) From 19677815271fcf510b628091a9d46cc9be8a741e Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 5 Oct 2018 17:48:37 +0200 Subject: [PATCH 104/147] add (failing) prism to jani test --- tests/storage/test_prism.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/storage/test_prism.py diff --git a/tests/storage/test_prism.py b/tests/storage/test_prism.py new file mode 100644 index 0000000..7ea3fa0 --- /dev/null +++ b/tests/storage/test_prism.py @@ -0,0 +1,12 @@ +import stormpy +import stormpy.logic +from helpers.helper import get_example_path +import pytest + +class TestPrism: + def test_prism_to_jani(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + orig_properties = stormpy.parse_properties_for_prism_program("P=? [F \"two\"]", program) + assert len(orig_properties) == 1 + jani_model, new_properties = program.to_jani(orig_properties) + assert len(new_properties) == len(orig_properties) From ad4ce3199fa48510d61c3df5d022455f355b6f9e Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 5 Oct 2018 18:12:42 +0200 Subject: [PATCH 105/147] add some variants of prism to jani --- tests/storage/test_prism.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/storage/test_prism.py b/tests/storage/test_prism.py index 7ea3fa0..cbb9788 100644 --- a/tests/storage/test_prism.py +++ b/tests/storage/test_prism.py @@ -4,9 +4,27 @@ from helpers.helper import get_example_path import pytest class TestPrism: - def test_prism_to_jani(self): + + def test_prism_to_jani_states(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + orig_properties = stormpy.parse_properties_for_prism_program("P=? [F s=7]", program) + assert len(orig_properties) == 1 + jani_model, new_properties = program.to_jani(orig_properties) + assert len(new_properties) == len(orig_properties) + + def test_prism_to_jani_labels(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) orig_properties = stormpy.parse_properties_for_prism_program("P=? [F \"two\"]", program) assert len(orig_properties) == 1 jani_model, new_properties = program.to_jani(orig_properties) assert len(new_properties) == len(orig_properties) + + + def test_prism_to_jani_repetitive(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + orig_properties = stormpy.parse_properties_for_prism_program("P=? [F \"two\"]", program) + jani_model, new_properties = program.to_jani(orig_properties) + assert len(new_properties) == len(orig_properties) + orig_properties = stormpy.parse_properties_for_prism_program("P=? [F s=7]", program) + jani_model, new_properties = program.to_jani(orig_properties, suffix = "2") + assert len(new_properties) == len(orig_properties) \ No newline at end of file From 8e193140442ccb9953a7a5fbe1d5030a31d6a25b Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 8 Oct 2018 22:17:57 +0200 Subject: [PATCH 106/147] some debugging facilities for jani model --- src/storage/jani.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index d18f766..e40076a 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -31,9 +31,11 @@ void define_jani(py::module& m) { .def("remove_constant", &Model::removeConstant, "remove a constant. Make sure the constant does not appear in the model.", "constant_name"_a) .def("get_automaton_index", &Model::getAutomatonIndex, "name"_a, "get index for automaton name") .def("replace_automaton", &Model::replaceAutomaton, "index"_a, "new_automaton"_a, "replace automaton at index") + .def("check_valid", &Model::checkValid, "Some basic checks to ensure validity") .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") .def("finalize", &Model::finalize,"finalizes the model. After this action, be careful changing the data structure.") + .def("to_dot", [](Model& model) {std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }) ; py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); From c73a5b77b3e8269fb83fe569e034fb6773336d1c Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 8 Oct 2018 22:18:36 +0200 Subject: [PATCH 107/147] explicit expression copies --- src/storage/expressions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index f43f8ae..4acdbfb 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -39,6 +39,7 @@ void define_expressions(py::module& m) { // Expression py::class_>(m, "Expression", "Holds an expression") + .def(py::init(), "other_expression"_a) .def("contains_variables", &storm::expressions::Expression::containsVariables, "Check if the expression contains variables.") .def("contains_variable", &storm::expressions::Expression::containsVariable, "Check if the expression contains any of the given variables.", py::arg("variables")) .def("get_variables" , &storm::expressions::Expression::getVariables, "Get the variables") From 233bf8b2ba879820027895dd27568221b109d095 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Mon, 8 Oct 2018 22:20:02 +0200 Subject: [PATCH 108/147] some explanation for iterating over MDPs and POMDPs --- doc/source/advanced_topics.rst | 1 + examples/exploration/01-exploration.py | 30 ++++++ examples/exploration/02-exploration.py | 38 +++++++ lib/stormpy/examples/files.py | 4 + lib/stormpy/examples/files/mdp/maze_2.nm | 126 +++++++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 examples/exploration/01-exploration.py create mode 100644 examples/exploration/02-exploration.py create mode 100644 lib/stormpy/examples/files/mdp/maze_2.nm diff --git a/doc/source/advanced_topics.rst b/doc/source/advanced_topics.rst index dc4a8e3..2ed8d80 100644 --- a/doc/source/advanced_topics.rst +++ b/doc/source/advanced_topics.rst @@ -9,6 +9,7 @@ This guide is a collection of examples meant to bridge the gap between the getti :caption: Contents: doc/building_models + doc/exploration doc/reward_models doc/shortest_paths doc/parametric_models diff --git a/examples/exploration/01-exploration.py b/examples/exploration/01-exploration.py new file mode 100644 index 0000000..fd22d2e --- /dev/null +++ b/examples/exploration/01-exploration.py @@ -0,0 +1,30 @@ +import stormpy +import stormpy.core + +import stormpy.examples +import stormpy.examples.files + + +def example_exploration_01(): + """ + Example to exploration of MDPs. + :return: + """ + program = stormpy.parse_prism_program(stormpy.examples.files.prism_pomdp_maze) + prop = "R=? [F \"goal\"]" + properties = stormpy.parse_properties_for_prism_program(prop, program, None) + model = stormpy.build_model(program, properties) + print(model.model_type) + + + for state in model.states: + if state.id in model.initial_states: + print(state) + for action in state.actions: + for transition in action.transitions: + print("From state {} by action {}, with probability {}, go to state {}".format(state, action, transition.value(), + transition.column)) + + +if __name__ == '__main__': + example_exploration_01() \ No newline at end of file diff --git a/examples/exploration/02-exploration.py b/examples/exploration/02-exploration.py new file mode 100644 index 0000000..951a29a --- /dev/null +++ b/examples/exploration/02-exploration.py @@ -0,0 +1,38 @@ +import stormpy +import stormpy.core + +import stormpy.examples +import stormpy.examples.files + + +def example_exploration_02(): + """ + Example to exploration of POMDPs. + :return: + """ + program = stormpy.parse_prism_program(stormpy.examples.files.prism_pomdp_maze) + prop = "R=? [F \"goal\"]" + properties = stormpy.parse_properties_for_prism_program(prop, program, None) + model = stormpy.build_model(program, properties) + print(model.model_type) + # Internally, POMDPs are just MDPs with additional observation information. + # Thus, data structure exploration for MDPs can be applied as before. + initial_state = model.initial_states[0] + + for state in model.states: + if state.id in model.initial_states: + print(state) + for action in state.actions: + for transition in action.transitions: + print("From state {} by action {}, with probability {}, go to state {}".format(state, action, transition.value(), + transition.column)) + + print(model.nr_observations) + + for state in model.states: + print("State {} has observation id {}".format(state.id, model.observations[state.id])) + + + +if __name__ == '__main__': + example_exploration_02() \ No newline at end of file diff --git a/lib/stormpy/examples/files.py b/lib/stormpy/examples/files.py index cb4718d..748b419 100644 --- a/lib/stormpy/examples/files.py +++ b/lib/stormpy/examples/files.py @@ -27,6 +27,10 @@ jani_dtmc_die = _path("dtmc", "die.jani") """Jani Version of Knuth Yao Die Example""" prism_mdp_coin_2_2 = _path("mdp", "coin2-2.nm") """Prism example for coin MDP""" +prism_mdp_maze = _path("mdp", "maze_2.nm") +"""Prism example for the maze MDP""" +prism_pomdp_maze = _path("pomdp", "maze_2.prism") +"""Prism example for the maze POMDP""" dft_galileo_hecs = _path("dft", "hecs.dft") """DFT example for HECS (Galileo format)""" dft_json_and = _path("dft", "and.json") diff --git a/lib/stormpy/examples/files/mdp/maze_2.nm b/lib/stormpy/examples/files/mdp/maze_2.nm new file mode 100644 index 0000000..396c48b --- /dev/null +++ b/lib/stormpy/examples/files/mdp/maze_2.nm @@ -0,0 +1,126 @@ + + +// maze example (POMDP) +// slightly extends that presented in +// Littman, Cassandra and Kaelbling +// Learning policies for partially observable environments: Scaling up +// Technical Report CS, Brown University +// gxn 29/01/16 +// Made into a MDP for documentation of stormpy. + +// state space (value of variable "s") + +// 0 1 2 3 4 +// 5 6 7 +// 8 9 10 +// 11 13 12 + +// 13 is the target + +mdp + + +module maze + + s : [-1..13]; + + // initialisation + [] s=-1 -> 1/13 : (s'=0) + + 1/13 : (s'=1) + + 1/13 : (s'=2) + + 1/13 : (s'=3) + + 1/13 : (s'=4) + + 1/13 : (s'=5) + + 1/13 : (s'=6) + + 1/13 : (s'=7) + + 1/13 : (s'=8) + + 1/13 : (s'=9) + + 1/13 : (s'=10) + + 1/13 : (s'=11) + + 1/13 : (s'=12); + + // moving around the maze + + [east] s=0 -> (s'=1); + [west] s=0 -> (s'=0); + [north] s=0 -> (s'=0); + [south] s=0 -> (s'=5); + + [east] s=1 -> (s'=2); + [west] s=1 -> (s'=0); + [north] s=1 -> (s'=1); + [south] s=1 -> (s'=1); + + [east] s=2 -> (s'=3); + [west] s=2 -> (s'=1); + [north] s=2 -> (s'=2); + [south] s=2 -> (s'=6); + + [east] s=3 -> (s'=4); + [west] s=3 -> (s'=2); + [north] s=3 -> (s'=3); + [south] s=3 -> (s'=3); + + [east] s=4 -> (s'=4); + [west] s=4 -> (s'=3); + [north] s=4 -> (s'=4); + [south] s=4 -> (s'=7); + + [east] s=5 -> (s'=5); + [west] s=5 -> (s'=5); + [north] s=5 -> (s'=0); + [south] s=5 -> (s'=8); + + [east] s=6 -> (s'=6); + [west] s=6 -> (s'=6); + [north] s=6 -> (s'=2); + [south] s=6 -> (s'=9); + + [east] s=7 -> (s'=7); + [west] s=7 -> (s'=7); + [north] s=7 -> (s'=4); + [south] s=7 -> (s'=10); + + [east] s=8 -> (s'=8); + [west] s=8 -> (s'=8); + [north] s=8 -> (s'=5); + [south] s=8 -> (s'=11); + + [east] s=9 -> (s'=9); + [west] s=9 -> (s'=9); + [north] s=9 -> (s'=6); + [south] s=9 -> (s'=13); + + [east] s=10 -> (s'=9); + [west] s=10 -> (s'=9); + [north] s=10 -> (s'=7); + [south] s=10 -> (s'=12); + [east] s=11 -> (s'=11); + [west] s=11 -> (s'=11); + [north] s=11 -> (s'=8); + [south] s=11 -> (s'=11); + + [east] s=12 -> (s'=12); + [west] s=12 -> (s'=12); + [north] s=12 -> (s'=10); + [south] s=12 -> (s'=12); + + // loop when we reach the target + [done] s=13 -> true; + +endmodule + +// reward structure (number of steps to reach the target) +rewards + + [east] true : 1; + [west] true : 1; + [north] true : 1; + [south] true : 1; + +endrewards + +// target observation +label "goal" = s=13; + + From 6b3033ca670d8e88da370326360755734fe30cab Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Tue, 9 Oct 2018 11:02:22 +0200 Subject: [PATCH 109/147] added missing file with description of exploration --- doc/source/doc/exploration.rst | 106 +++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 doc/source/doc/exploration.rst diff --git a/doc/source/doc/exploration.rst b/doc/source/doc/exploration.rst new file mode 100644 index 0000000..5285b2a --- /dev/null +++ b/doc/source/doc/exploration.rst @@ -0,0 +1,106 @@ +**************** +Exploring Models +**************** + +Background +===================== + +Often, stormpy is used as a testbed for new algorithms. +An essential step is to transfer the (low-level) descriptions of an MDP or other state-based model into +an own algorithm. In this section, we discuss some of the functionality. + +Reading MDPs +===================== + +.. seealso:: `01-exploration.py `_ + +In :doc:`../getting_started`, we briefly iterated over a DTMC. In this section, we explore an MDP:: + + >>> import doctest + >>> doctest.ELLIPSIS_MARKER = '-etc-' # doctest:+ELLIPSIS + >>> import stormpy + >>> import stormpy.examples + >>> import stormpy.examples.files + >>> program = stormpy.parse_prism_program(stormpy.examples.files.prism_mdp_maze) + >>> prop = "R=? [F \"goal\"]" + + >>> properties = stormpy.parse_properties_for_prism_program(prop, program, None) + >>> model = stormpy.build_model(program, properties) + +The iteration over the model is as before, but now, for every action, we can have several transitions:: + + >>> for state in model.states: + ... if state.id in model.initial_states: + ... print("State {} is initial".format(state.id)) + ... for action in state.actions: + ... for transition in action.transitions: + ... print("From state {} by action {}, with probability {}, go to state {}".format(state, action, transition.value(), transition.column)) + -etc- + +The output (omitted for brievety) contains sentences like: + + From state 1 by action 0, with probability 1.0, go to state 2 + From state 1 by action 1, with probability 1.0, go to state 1 + + + +Internally, storm can hold hints to the origin of the actions, which may be helpful to give meaning and for debugging. +As the availability and the encoding of this data depends on the input model, we discuss these features in :doc:`highlevel_models`. + + +Storm currently supports deterministic rewards on states or actions. More information can be found in that :doc:`reward_models`. + + +Reading POMDPs +====================== +.. seealso:: `02-exploration.py `_ + + +Internally, POMDPs extend MDPs. Thus, iterating over the MDP is done as before. + + >>> import stormpy + >>> import stormpy.examples + >>> import stormpy.examples.files + >>> program = stormpy.parse_prism_program(stormpy.examples.files.prism_pomdp_maze) + >>> prop = "R=? [F \"goal\"]" + + >>> properties = stormpy.parse_properties_for_prism_program(prop, program, None) + >>> model = stormpy.build_model(program, properties) + +Indeed, all that changed in the code above is the example we use. +And, that the model type now is a POMDP:: + + >>> print(model.model_type) + ModelType.POMDP + +Additionally, POMDPs have a set of observations, which are internally just numbered by an integer from 0 to the number of observations -1 :: + + >>> print(model.nr_observations) + 8 + >>> for state in model.states: + ... print("State {} has observation id {}".format(state.id, model.observations[state.id])) + State 0 has observation id 6 + State 1 has observation id 1 + State 2 has observation id 4 + State 3 has observation id 7 + State 4 has observation id 4 + State 5 has observation id 3 + State 6 has observation id 0 + State 7 has observation id 0 + State 8 has observation id 0 + State 9 has observation id 0 + State 10 has observation id 0 + State 11 has observation id 0 + State 12 has observation id 2 + State 13 has observation id 2 + State 14 has observation id 4 + State 15 has observation id 5 + + + + + +Reading MAs +====================== + +To be continued... \ No newline at end of file From befee6332f4d16b79c9643afaa744c2a88e9a559 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 24 Oct 2018 17:49:20 +0200 Subject: [PATCH 110/147] Added simple filtering for initial states --- src/core/result.cpp | 24 ++++++++++++++++++++++-- tests/core/test_modelchecking.py | 20 ++++++++++++++++++++ tests/core/test_transformation.py | 16 ++++++++-------- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/core/result.cpp b/src/core/result.cpp index 4e6728d..9827950 100644 --- a/src/core/result.cpp +++ b/src/core/result.cpp @@ -8,6 +8,17 @@ #include "storm/models/symbolic/StandardRewardModel.h" +template +std::shared_ptr createFilterInitialStatesSparse(std::shared_ptr> model) { + return std::make_unique(model->getInitialStates()); +} + +template +std::shared_ptr createFilterInitialStatesSymbolic(std::shared_ptr> model) { + return std::make_unique>(model->getReachableStates(), model->getInitialStates()); +} + + // Define python bindings void define_result(py::module& m) { @@ -35,6 +46,7 @@ void define_result(py::module& m) { .def("as_explicit_parametric_quantitative", [](storm::modelchecker::CheckResult const& result) { return result.asExplicitQuantitativeCheckResult(); }, "Convert into explicit quantitative result") + .def("filter", &storm::modelchecker::CheckResult::filter, py::arg("filter"), "Filter the result") .def("__str__", [](storm::modelchecker::CheckResult const& result) { std::stringstream stream; result.writeToStream(stream); @@ -45,16 +57,20 @@ void define_result(py::module& m) { // QualitativeCheckResult py::class_> qualitativeCheckResult(m, "_QualitativeCheckResult", "Abstract class for qualitative model checking results", checkResult); py::class_>(m, "ExplicitQualitativeCheckResult", "Explicit qualitative model checking result", qualitativeCheckResult) - .def("at", [](storm::modelchecker::ExplicitQualitativeCheckResult const& result, storm::storage::sparse::state_type state) { + .def("at", [](storm::modelchecker::ExplicitQualitativeCheckResult const& result, storm::storage::sparse::state_type state) { return result[state]; }, py::arg("state"), "Get result for given state") - .def("get_truth_values", &storm::modelchecker::ExplicitQualitativeCheckResult::getTruthValuesVector, "Get BitVector representing the truth values") + .def("get_truth_values", &storm::modelchecker::ExplicitQualitativeCheckResult::getTruthValuesVector, "Get BitVector representing the truth values") ; py::class_, std::shared_ptr>>(m, "SymbolicQualitativeCheckResult", "Symbolic qualitative model checking result", qualitativeCheckResult) ; // QuantitativeCheckResult py::class_, std::shared_ptr>> quantitativeCheckResult(m, "_QuantitativeCheckResult", "Abstract class for quantitative model checking results", checkResult); + quantitativeCheckResult.def_property_readonly("min", &storm::modelchecker::QuantitativeCheckResult::getMin, "Minimal value") + .def_property_readonly("max", &storm::modelchecker::QuantitativeCheckResult::getMax, "Maximal value") + ; + py::class_, std::shared_ptr>>(m, "ExplicitQuantitativeCheckResult", "Explicit quantitative model checking result", quantitativeCheckResult) .def("at", [](storm::modelchecker::ExplicitQuantitativeCheckResult const& result, storm::storage::sparse::state_type state) { return result[state]; @@ -81,5 +97,9 @@ void define_result(py::module& m) { .def("get_values", &storm::modelchecker::HybridQuantitativeCheckResult::getExplicitValueVector, "Get model checking result values for all states") ; + + m.def("create_filter_initial_states_sparse", &createFilterInitialStatesSparse, "Create a filter for the initial states on a sparse model", py::arg("model")); + m.def("create_filter_initial_states_symbolic", &createFilterInitialStatesSymbolic, "Create a filter for the initial states on a symbolic model", py::arg("model")); + } diff --git a/tests/core/test_modelchecking.py b/tests/core/test_modelchecking.py index 1d4b15f..2868953 100644 --- a/tests/core/test_modelchecking.py +++ b/tests/core/test_modelchecking.py @@ -118,6 +118,22 @@ class TestModelChecking: result = stormpy.model_checking(model, formulas[0]) assert math.isclose(result.at(initial_state), 4.166666667) + def test_filter(self): + program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"one\" ]", program) + model = stormpy.build_model(program, formulas) + assert model.nr_states == 13 + assert model.nr_transitions == 20 + assert len(model.initial_states) == 1 + initial_state = model.initial_states[0] + assert initial_state == 0 + result = stormpy.model_checking(model, formulas[0]) + assert math.isclose(result.at(initial_state), 1 / 6) + filter = stormpy.create_filter_initial_states_sparse(model) + result.filter(filter) + assert result.min == result.max + assert math.isclose(result.min, 1 / 6) + def test_model_checking_prism_dd_dtmc(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) formulas = stormpy.parse_properties_for_prism_program("P=? [ F \"one\" ]", program) @@ -126,6 +142,10 @@ class TestModelChecking: assert model.nr_transitions == 20 result = stormpy.check_model_dd(model, formulas[0]) assert type(result) is stormpy.SymbolicQuantitativeCheckResult + filter = stormpy.create_filter_initial_states_symbolic(model) + result.filter(filter) + assert result.min == result.max + assert math.isclose(result.min, 1 / 6, rel_tol=1e-6) def test_model_checking_prism_hybrid_dtmc(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "die.pm")) diff --git a/tests/core/test_transformation.py b/tests/core/test_transformation.py index 562f85f..e1a322e 100644 --- a/tests/core/test_transformation.py +++ b/tests/core/test_transformation.py @@ -6,18 +6,18 @@ from helpers.helper import get_example_path class TestTransformation: def test_transform_symbolic_dtmc_to_sparse(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "crowds5_5.pm")) - model = stormpy.build_symbolic_model(program) - assert model.nr_states == 8607 - assert model.nr_transitions == 15113 - assert model.model_type == stormpy.ModelType.DTMC - assert not model.supports_parameters - assert type(model) is stormpy.SymbolicSylvanDtmc - symbolic_model = stormpy.transform_to_sparse_model(model) + symbolic_model = stormpy.build_symbolic_model(program) assert symbolic_model.nr_states == 8607 assert symbolic_model.nr_transitions == 15113 assert symbolic_model.model_type == stormpy.ModelType.DTMC assert not symbolic_model.supports_parameters - assert type(symbolic_model) is stormpy.SparseDtmc + assert type(symbolic_model) is stormpy.SymbolicSylvanDtmc + sparse_model = stormpy.transform_to_sparse_model(symbolic_model) + assert sparse_model.nr_states == 8607 + assert sparse_model.nr_transitions == 15113 + assert sparse_model.model_type == stormpy.ModelType.DTMC + assert not sparse_model.supports_parameters + assert type(sparse_model) is stormpy.SparseDtmc def test_transform_symbolic_parametric_dtmc_to_sparse(self): program = stormpy.parse_prism_program(get_example_path("pdtmc", "parametric_die.pm")) From 4ab6277e3f95984044eb51441e33743f5e4fc59d Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 24 Oct 2018 17:50:26 +0200 Subject: [PATCH 111/147] Fixed some typos --- doc/source/doc/building_models.rst | 2 +- doc/source/doc/exploration.rst | 5 ++--- doc/source/doc/parametric_models.rst | 1 - doc/source/doc/shortest_paths.rst | 4 ++-- doc/source/getting_started.rst | 14 +++++++------- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/doc/source/doc/building_models.rst b/doc/source/doc/building_models.rst index 690e491..02229b3 100644 --- a/doc/source/doc/building_models.rst +++ b/doc/source/doc/building_models.rst @@ -17,7 +17,7 @@ We use some standard examples:: >>> import stormpy.examples >>> import stormpy.examples.files -Storm supports the DRN format. +Storm supports the explicit DRN format. From this, models can be built directly:: >>> path = stormpy.examples.files.drn_ctmc_dft diff --git a/doc/source/doc/exploration.rst b/doc/source/doc/exploration.rst index 5285b2a..383d4f2 100644 --- a/doc/source/doc/exploration.rst +++ b/doc/source/doc/exploration.rst @@ -37,7 +37,7 @@ The iteration over the model is as before, but now, for every action, we can hav ... print("From state {} by action {}, with probability {}, go to state {}".format(state, action, transition.value(), transition.column)) -etc- -The output (omitted for brievety) contains sentences like: +The output (omitted for brievety) contains sentences like:: From state 1 by action 0, with probability 1.0, go to state 2 From state 1 by action 1, with probability 1.0, go to state 1 @@ -48,7 +48,7 @@ Internally, storm can hold hints to the origin of the actions, which may be help As the availability and the encoding of this data depends on the input model, we discuss these features in :doc:`highlevel_models`. -Storm currently supports deterministic rewards on states or actions. More information can be found in that :doc:`reward_models`. +Storm currently supports deterministic rewards on states or actions. More information can be found in :doc:`reward_models`. Reading POMDPs @@ -63,7 +63,6 @@ Internally, POMDPs extend MDPs. Thus, iterating over the MDP is done as before. >>> import stormpy.examples.files >>> program = stormpy.parse_prism_program(stormpy.examples.files.prism_pomdp_maze) >>> prop = "R=? [F \"goal\"]" - >>> properties = stormpy.parse_properties_for_prism_program(prop, program, None) >>> model = stormpy.build_model(program, properties) diff --git a/doc/source/doc/parametric_models.rst b/doc/source/doc/parametric_models.rst index c4376c4..d16b597 100644 --- a/doc/source/doc/parametric_models.rst +++ b/doc/source/doc/parametric_models.rst @@ -18,7 +18,6 @@ If the constants only influence the probabilities or rates, but not the topology >>> prism_program = stormpy.parse_prism_program(path) >>> formula_str = "P=? [F s=7 & d=2]" >>> properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program) - >>> model = stormpy.build_parametric_model(prism_program, properties) >>> parameters = model.collect_probability_parameters() >>> for x in parameters: diff --git a/doc/source/doc/shortest_paths.rst b/doc/source/doc/shortest_paths.rst index aca96fa..a727950 100644 --- a/doc/source/doc/shortest_paths.rst +++ b/doc/source/doc/shortest_paths.rst @@ -29,7 +29,7 @@ As in :doc:`../getting_started`, we import some required modules and build a mod >>> model = stormpy.build_model(prism_program) -We also import the `ShortestPathsGenerator`:: +We also import the ``ShortestPathsGenerator``:: >>> from stormpy.utility import ShortestPathsGenerator @@ -40,7 +40,7 @@ and choose a target state (by its ID) to which we want to compute the shortest p It is also possible to specify a set of target states (as a list, e.g., ``[8, 10, 11]``) or a label in the model if applicable (e.g., ``"observe0Greater1"``). For simplicity, we will stick to using a single state for now. -We initialize a `ShortestPathsGenerator` instance:: +We initialize a ``ShortestPathsGenerator`` instance:: >>> spg = ShortestPathsGenerator(model, state_id) diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index abf1c57..e35acc1 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -41,7 +41,7 @@ With this, we can now import the path of our prism file:: >>> path = stormpy.examples.files.prism_dtmc_die >>> prism_program = stormpy.parse_prism_program(path) -The `prism_program` can be translated into Markov chains:: +The ``prism_program`` can be translated into a Markov chain:: >>> model = stormpy.build_model(prism_program) >>> print("Number of states: {}".format(model.nr_states)) @@ -56,7 +56,7 @@ Moreover, initial states and deadlocks are indicated with a labelling function. >>> print("Labels: {}".format(model.labeling.get_labels())) Labels: ... -We will investigate ways to examine the model in more detail in :ref:`getting-started-investigating-the-model`. +We will investigate ways to examine the model in more detail later in :ref:`getting-started-investigating-the-model`. .. _getting-started-building-properties: @@ -65,7 +65,7 @@ Building properties .. seealso:: `02-getting-started.py `_ Storm takes properties in the prism-property format. -To express that one is interested in the reachability of any state where the prism program variable s is 2, one would formulate:: +To express that one is interested in the reachability of any state where the prism program variable ``s`` is 2, one would formulate:: P=? [F s=2] @@ -76,7 +76,7 @@ Stormpy can be used to parse this. As the variables in the property refer to a p Notice that properties is now a list of properties containing a single element. -However, if we build the model as before, then the appropriate information that the variable s=2 in some states is not present. +However, if we build the model as before, then the appropriate information that the variable ``s=2`` in some states is not present. In order to label the states accordingly, we should notify Storm upon building the model that we would like to preserve given properties. Storm will then add the labels accordingly:: @@ -85,7 +85,7 @@ Storm will then add the labels accordingly:: Labels in the model: ['(s = 2)', 'deadlock', 'init'] Model building however now behaves slightly different: Only the properties passed are preserved, which means that model building might skip parts of the model. -In particular, to check the probability of eventually reaching a state x where s=2, successor states of x are not relevant:: +In particular, to check the probability of eventually reaching a state ``x`` where ``s=2``, successor states of ``x`` are not relevant:: >>> print("Number of states: {}".format(model.nr_states)) Number of states: 8 @@ -94,7 +94,7 @@ If we consider another property, however, such as:: P=? [F s=7 & d=2] -then Storm is only skipping exploration of successors of the particular state y where s=7 and d=2. In this model, state y has a self-loop, so effectively, the whole model is explored. +then Storm is only skipping exploration of successors of the particular state ``y`` where ``s=7`` and ``d=2``. In this model, state ``y`` has a self-loop, so effectively, the whole model is explored. .. _getting-started-checking-properties: @@ -180,7 +180,7 @@ Thus:: ... assert len(state.actions) <= 1 -We can also check if a state is indeed an initial state. Notice that model.initial_states contains state ids, not states.:: +We can also check if a state is indeed an initial state. Notice that ``model.initial_states`` contains state ids, not states.:: >>> for state in model.states: ... if state.id in model.initial_states: From c714cb68f51a7e3c9072a352e0b680cfb4922adb Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 24 Oct 2018 17:50:47 +0200 Subject: [PATCH 112/147] Added documentation for engines --- doc/source/advanced_topics.rst | 1 + doc/source/doc/engines.rst | 82 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 doc/source/doc/engines.rst diff --git a/doc/source/advanced_topics.rst b/doc/source/advanced_topics.rst index 2ed8d80..bc32885 100644 --- a/doc/source/advanced_topics.rst +++ b/doc/source/advanced_topics.rst @@ -9,6 +9,7 @@ This guide is a collection of examples meant to bridge the gap between the getti :caption: Contents: doc/building_models + doc/engines doc/exploration doc/reward_models doc/shortest_paths diff --git a/doc/source/doc/engines.rst b/doc/source/doc/engines.rst new file mode 100644 index 0000000..f145e5b --- /dev/null +++ b/doc/source/doc/engines.rst @@ -0,0 +1,82 @@ +*************** +Engines +*************** + +Background +===================== + +Storm supports different engines for building and checking a model. A detailed comparison of the different engines provided in Storm can be found on the `Storm website `_. + + +Sparse engine +=============================== + +In all of the examples so far we used the default sparse engine: + + >>> import stormpy.examples + >>> import stormpy.examples.files + >>> prism_program = stormpy.parse_prism_program(stormpy.examples.files.prism_dtmc_die) + >>> properties = stormpy.parse_properties_for_prism_program('P=? [F "one"]', prism_program) + >>> sparse_model = stormpy.build_sparse_model(prism_program, properties) + >>> print(type(sparse_model)) + + >>> print("Number of states: {}".format(sparse_model.nr_states)) + Number of states: 13 + >>> print("Number of transitions: {}".format(sparse_model.nr_transitions)) + Number of transitions: 20 + +The model checking was also done in the sparse engine: + + >>> sparse_result = stormpy.check_model_sparse(sparse_model, properties[0]) + >>> initial_state = sparse_model.initial_states[0] + >>> print(sparse_result.at(initial_state)) + 0.16666666666666666 + + +Symbolic engine +=============================== + +Instead of using the sparse engine, one can also use a symbolic representation in terms of `binary decision diagrams (BDDs)`. +To use the symbolic (dd) engine, we use the symbolic versions for the building and model checking: + + >>> symbolic_model = stormpy.build_symbolic_model(prism_program, properties) + >>> print(type(symbolic_model)) + + >>> print("Number of states: {}".format(symbolic_model.nr_states)) + Number of states: 13 + >>> print("Number of transitions: {}".format(symbolic_model.nr_transitions)) + Number of transitions: 20 + >>> symbolic_result = stormpy.check_model_dd(symbolic_model, properties[0]) + >>> print(symbolic_result) + [0, 1] (range) + +We can also filter the computed results and only consider the initial states: + + >>> filter = stormpy.create_filter_initial_states_symbolic(symbolic_model) + >>> symbolic_result.filter(filter) + >>> print(symbolic_result.min) + 0.16666650772094727 + +It is also possible to first build the model symbolically and then transform it into a sparse model: + + >>> print(type(symbolic_model)) + + >>> transformed_model = stormpy.transform_to_sparse_model(symbolic_model) + >>> print(type(transformed_model)) + + + +Hybrid engine +=============================== + +A third possibility is to use the hybrid engine, a combination of sparse and dd engines. +It first builds the model symbolically. +The actual model checking is then performed with the engine which is deemed most suitable for the given task. + + >>> print(type(symbolic_model)) + + >>> hybrid_result = stormpy.check_model_hybrid(symbolic_model, properties[0]) + >>> filter = stormpy.create_filter_initial_states_symbolic(symbolic_model) + >>> hybrid_result.filter(filter) + >>> print(hybrid_result) + 0.166667 From 5aab3cbe3e8979d6141e61145af9176f43e03ac2 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 11:37:15 +0100 Subject: [PATCH 113/147] support for properties and raw formulae in symbolic model creation --- lib/stormpy/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index fa6fabe..0f81f35 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -163,7 +163,7 @@ def build_symbolic_model(symbolic_description, properties=None): raise StormError("Program still contains undefined constants") if properties: - formulae = [prop.raw_formula for prop in properties] + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] intermediate = core._build_symbolic_model_from_symbolic_description(symbolic_description, formulae) else: intermediate = core._build_symbolic_model_from_symbolic_description(symbolic_description) @@ -182,7 +182,7 @@ def build_symbolic_parametric_model(symbolic_description, properties=None): raise StormError("Program still contains undefined constants") if properties: - formulae = [prop.raw_formula for prop in properties] + formulae = [(prop.raw_formula if isinstance(property, Property) else prop) for prop in properties] intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description, formulae) else: intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description) From 30f6b4395d163af5937bade4e00fe93f295bf8c4 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 11:39:16 +0100 Subject: [PATCH 114/147] model checking routines take optional environment --- lib/stormpy/__init__.py | 22 +++++++++++----------- src/core/modelchecking.cpp | 25 +++++++++++++------------ src/mod_core.cpp | 3 ++- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 0f81f35..b3caa25 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -252,7 +252,7 @@ def perform_symbolic_bisimulation(model, properties): return core._perform_symbolic_bisimulation(model, formulae, bisimulation_type) -def model_checking(model, property, only_initial_states=False, extract_scheduler=False): +def model_checking(model, property, only_initial_states=False, extract_scheduler=False, environment=Environment()): """ Perform model checking on model for property. :param model: Model. @@ -263,10 +263,10 @@ def model_checking(model, property, only_initial_states=False, extract_scheduler :rtype: CheckResult """ return check_model_sparse(model, property, only_initial_states=only_initial_states, - extract_scheduler=extract_scheduler) + extract_scheduler=extract_scheduler, environment=environment) -def check_model_sparse(model, property, only_initial_states=False, extract_scheduler=False): +def check_model_sparse(model, property, only_initial_states=False, extract_scheduler=False, environment=Environment()): """ Perform model checking on model for property. :param model: Model. @@ -284,14 +284,14 @@ def check_model_sparse(model, property, only_initial_states=False, extract_sched if model.supports_parameters: task = core.ParametricCheckTask(formula, only_initial_states) task.set_produce_schedulers(extract_scheduler) - return core._parametric_model_checking_sparse_engine(model, task) + return core._parametric_model_checking_sparse_engine(model, task, environment=environment) else: task = core.CheckTask(formula, only_initial_states) task.set_produce_schedulers(extract_scheduler) - return core._model_checking_sparse_engine(model, task) + return core._model_checking_sparse_engine(model, task, environment=environment) -def check_model_dd(model, property, only_initial_states=False): +def check_model_dd(model, property, only_initial_states=False, environment=Environment()): """ Perform model checking using dd engine. :param model: Model. @@ -307,13 +307,13 @@ def check_model_dd(model, property, only_initial_states=False): if model.supports_parameters: task = core.ParametricCheckTask(formula, only_initial_states) - return core._parametric_model_checking_dd_engine(model, task) + return core._parametric_model_checking_dd_engine(model, task, environment=environment) else: task = core.CheckTask(formula, only_initial_states) - return core._model_checking_dd_engine(model, task) + return core._model_checking_dd_engine(model, task, environment=environment) -def check_model_hybrid(model, property, only_initial_states=False): +def check_model_hybrid(model, property, only_initial_states=False, environment=Environment()): """ Perform model checking using hybrid engine. :param model: Model. @@ -329,10 +329,10 @@ def check_model_hybrid(model, property, only_initial_states=False): if model.supports_parameters: task = core.ParametricCheckTask(formula, only_initial_states) - return core._parametric_model_checking_hybrid_engine(model, task) + return core._parametric_model_checking_hybrid_engine(model, task, environment=environment) else: task = core.CheckTask(formula, only_initial_states) - return core._model_checking_hybrid_engine(model, task) + return core._model_checking_hybrid_engine(model, task, environment=environment) def transform_to_sparse_model(model): """ diff --git a/src/core/modelchecking.cpp b/src/core/modelchecking.cpp index 862d4c3..684700a 100644 --- a/src/core/modelchecking.cpp +++ b/src/core/modelchecking.cpp @@ -2,26 +2,27 @@ #include "result.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/modelchecker/results/CheckResult.h" +#include "storm/environment/Environment.h" template using CheckTask = storm::modelchecker::CheckTask; // Thin wrapper for model checking using sparse engine template -std::shared_ptr modelCheckingSparseEngine(std::shared_ptr> model, CheckTask const& task) { - return storm::api::verifyWithSparseEngine(model, task); +std::shared_ptr modelCheckingSparseEngine(std::shared_ptr> model, CheckTask const& task, storm::Environment const& env) { + return storm::api::verifyWithSparseEngine(env, model, task); } // Thin wrapper for model checking using dd engine template -std::shared_ptr modelCheckingDdEngine(std::shared_ptr> model, CheckTask const& task) { - return storm::api::verifyWithDdEngine(model, task); +std::shared_ptr modelCheckingDdEngine(std::shared_ptr> model, CheckTask const& task, storm::Environment const& env) { + return storm::api::verifyWithDdEngine(env, model, task); } // Thin wrapper for model checking using hybrid engine template -std::shared_ptr modelCheckingHybridEngine(std::shared_ptr> model, CheckTask const& task) { - return storm::api::verifyWithHybridEngine(model, task); +std::shared_ptr modelCheckingHybridEngine(std::shared_ptr> model, CheckTask const& task, storm::Environment const& env) { + return storm::api::verifyWithHybridEngine(env, model, task); } // Thin wrapper for computing prob01 states @@ -56,12 +57,12 @@ void define_modelchecking(py::module& m) { ; // Model checking - m.def("_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform model checking using the sparse engine", py::arg("model"), py::arg("task")); - m.def("_parametric_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform parametric model checking using the sparse engine", py::arg("model"), py::arg("task")); - m.def("_model_checking_dd_engine", &modelCheckingDdEngine, "Perform model checking using the dd engine", py::arg("model"), py::arg("task")); - m.def("_parametric_model_checking_dd_engine", &modelCheckingDdEngine, "Perform parametric model checking using the dd engine", py::arg("model"), py::arg("task")); - m.def("_model_checking_hybrid_engine", &modelCheckingHybridEngine, "Perform model checking using the hybrid engine", py::arg("model"), py::arg("task")); - m.def("_parametric_model_checking_hybrid_engine", &modelCheckingHybridEngine, "Perform parametric model checking using the hybrid engine", py::arg("model"), py::arg("task")); + m.def("_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform model checking using the sparse engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment()); + m.def("_parametric_model_checking_sparse_engine", &modelCheckingSparseEngine, "Perform parametric model checking using the sparse engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment()); + m.def("_model_checking_dd_engine", &modelCheckingDdEngine, "Perform model checking using the dd engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment()); + m.def("_parametric_model_checking_dd_engine", &modelCheckingDdEngine, "Perform parametric model checking using the dd engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment()); + m.def("_model_checking_hybrid_engine", &modelCheckingHybridEngine, "Perform model checking using the hybrid engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment()); + m.def("_parametric_model_checking_hybrid_engine", &modelCheckingHybridEngine, "Perform parametric model checking using the hybrid engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment()); m.def("_compute_prob01states_double", &computeProb01, "Compute prob-0-1 states", py::arg("model"), py::arg("phi_states"), py::arg("psi_states")); m.def("_compute_prob01states_rationalfunc", &computeProb01, "Compute prob-0-1 states", py::arg("model"), py::arg("phi_states"), py::arg("psi_states")); m.def("_compute_prob01states_min_double", &computeProb01min, "Compute prob-0-1 states (min)", py::arg("model"), py::arg("phi_states"), py::arg("psi_states")); diff --git a/src/mod_core.cpp b/src/mod_core.cpp index d3a752e..1c351f0 100644 --- a/src/mod_core.cpp +++ b/src/mod_core.cpp @@ -18,7 +18,9 @@ PYBIND11_MODULE(core, m) { // options.disable_function_signatures(); #endif + define_environment(m); define_core(m); + define_property(m); define_parse(m); define_build(m); @@ -30,6 +32,5 @@ PYBIND11_MODULE(core, m) { define_bisimulation(m); define_input(m); define_graph_constraints(m); - define_environment(m); define_transformation(m); } From 183f0cf8438bfd065f73df5848b5892589dc0ae1 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 11:39:42 +0100 Subject: [PATCH 115/147] extended environment --- src/core/environment.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/core/environment.cpp b/src/core/environment.cpp index 6e6f8f8..f9c1596 100644 --- a/src/core/environment.cpp +++ b/src/core/environment.cpp @@ -19,15 +19,34 @@ void define_environment(py::module& m) { .value("topological", storm::solver::EquationSolverType::Topological) ; + py::enum_(m, "MinMaxMethod", "Method for min-max equation systems") + .value("policy_iteration", storm::solver::MinMaxMethod::PolicyIteration) + .value("value_iteration", storm::solver::MinMaxMethod::ValueIteration) + .value("linear_programming", storm::solver::MinMaxMethod::LinearProgramming) + .value("topological", storm::solver::MinMaxMethod::Topological) + .value("rational_search", storm::solver::MinMaxMethod::RationalSearch) + .value("interval_iteration", storm::solver::MinMaxMethod::IntervalIteration) + .value("sound_value_iteration", storm::solver::MinMaxMethod::SoundValueIteration) + .value("topological_cuda", storm::solver::MinMaxMethod::TopologicalCuda) + ; + + py::class_(m, "Environment", "Environment") .def(py::init<>(), "Construct default environment") - .def_property_readonly("solver_environment", [](storm::Environment& env) {return env.solver();}, "solver part of environment") + .def_property_readonly("solver_environment", [](storm::Environment& env) -> auto& {return env.solver();}, "solver part of environment") ; py::class_(m, "SolverEnvironment", "Environment for solvers") .def("set_force_sound", &storm::SolverEnvironment::setForceSoundness, "force soundness", py::arg("new_value") = true) .def("set_linear_equation_solver_type", &storm::SolverEnvironment::setLinearEquationSolverType, "set solver type to use", py::arg("new_value"), py::arg("set_from_default") = false) + .def_property_readonly("minmax_solver_environment", [](storm::SolverEnvironment& senv) -> auto& { return senv.minMax(); }) ; + py::class_(m, "MinMaxSolverEnvironment", "Environment for Min-Max-Solvers") + .def_property("method", &storm::MinMaxSolverEnvironment::getMethod, [](storm::MinMaxSolverEnvironment& mmenv, storm::solver::MinMaxMethod const& m) { mmenv.setMethod(m, false); } ) + .def_property("precision", &storm::MinMaxSolverEnvironment::getPrecision, &storm::MinMaxSolverEnvironment::setPrecision); + + + } From 297f5457a307090a0e8bb664f798cb760c6caef4 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 12:52:17 +0100 Subject: [PATCH 116/147] improved access to counterexamples --- src/core/counterexample.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/counterexample.cpp b/src/core/counterexample.cpp index b3eb054..b4cb1e2 100644 --- a/src/core/counterexample.cpp +++ b/src/core/counterexample.cpp @@ -1,3 +1,5 @@ +#include + #include "counterexample.h" #include "storm/environment/Environment.h" #include "storm-counterexamples/api/counterexamples.h" @@ -11,9 +13,15 @@ void define_counterexamples(py::module& m) { py::class_>(m, "FlatSet", "Container to pass to program") .def(py::init<>()) + .def(py::init>(), "other"_a) .def("insert", [](boost::container::flat_set& flatset, uint64_t value) {flatset.insert(value);}) + .def("is_subset_of", [](boost::container::flat_set const& left, boost::container::flat_set const& right) {return std::includes(right.begin(), right.end(), left.begin(), left.end()); }) + .def("insert_set", [](boost::container::flat_set& left, boost::container::flat_set const& additional) { for(auto const& i : additional) {left.insert(i);}}) .def("__str__", [](boost::container::flat_set const& set) { std::stringstream str; str << "["; for(auto const& i : set) { str << i << ", ";} str << "]"; return str.str(); }) .def("__len__", [](boost::container::flat_set const& set) { return set.size();}) + .def("__iter__", [](boost::container::flat_set &v) { + return py::make_iterator(v.begin(), v.end()); + }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ ; using CexGeneratorStats = SMTMinimalLabelSetGenerator::GeneratorStats; From 1b75c232572ea3aa4f4ebc1fdf26964a839e0c83 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 12:53:26 +0100 Subject: [PATCH 117/147] updated formula operator access --- src/logic/formulae.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/logic/formulae.cpp b/src/logic/formulae.cpp index 31d1e31..f720d03 100644 --- a/src/logic/formulae.cpp +++ b/src/logic/formulae.cpp @@ -75,9 +75,13 @@ void define_formulae(py::module& m) { .def("set_bound", [](storm::logic::OperatorFormula& f, storm::logic::ComparisonType comparisonType, storm::expressions::Expression const& bound) { f.setBound(storm::logic::Bound(comparisonType, bound)); }, "Set bound", py::arg("comparison_type"), py::arg("bound")) + .def("remove_bound", &storm::logic::OperatorFormula::removeBound) .def_property_readonly("has_optimality_type", &storm::logic::OperatorFormula::hasOptimalityType, "Flag if an optimality type is present") .def_property_readonly("optimality_type", &storm::logic::OperatorFormula::getOptimalityType, "Flag for the optimality type") - ; + .def("set_optimality_type", &storm::logic::OperatorFormula::setOptimalityType, "set the optimality type (use remove optimiality type for clearing)", "new_optimality_type"_a) + .def("remove_optimality_type", &storm::logic::OperatorFormula::removeOptimalityType, "remove the optimality type") + + ; py::class_>(m, "TimeOperator", "The time operator", operatorFormula); py::class_>(m, "LongRunAvarageOperator", "Long run average operator", operatorFormula); py::class_>(m, "ProbabilityOperator", "Probability operator", operatorFormula) From 65147aec12f988e36777f5c8e841389c068f6a95 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 12:54:35 +0100 Subject: [PATCH 118/147] expressions: operators changed to capital (breaks backward compatibility) --- src/storage/expressions.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/storage/expressions.cpp b/src/storage/expressions.cpp index 4acdbfb..5345cae 100644 --- a/src/storage/expressions.cpp +++ b/src/storage/expressions.cpp @@ -52,19 +52,19 @@ void define_expressions(py::module& m) { .def("substitute", [](Expression const& expr, std::map const& map) { return expr.substitute(map);}, "substitution_map"_a) .def("__str__", &storm::expressions::Expression::toString, "To string") - .def_static("plus", [](Expression const& lhs, Expression const& rhs) {return lhs + rhs;}) - .def_static("minus", [](Expression const& lhs, Expression const& rhs) {return lhs - rhs;}) - .def_static("multiply", [](Expression const& lhs, Expression const& rhs) {return lhs * rhs;}) - .def_static("and", [](Expression const& lhs, Expression const& rhs) {return lhs && rhs;}) - .def_static("or", [](Expression const& lhs, Expression const& rhs) {return lhs || rhs;}) - .def_static("geq", [](Expression const& lhs, Expression const& rhs) {return lhs >= rhs;}) - .def_static("eq", [](Expression const& lhs, Expression const& rhs) {return lhs == rhs;}) - .def_static("neq", [](Expression const& lhs, Expression const& rhs) {return lhs != rhs;}) - .def_static("greater", [](Expression const& lhs, Expression const& rhs) {return lhs > rhs;}) - .def_static("less", [](Expression const& lhs, Expression const& rhs) {return lhs < rhs;}) - .def_static("leq", [](Expression const& lhs, Expression const& rhs) {return lhs <= rhs;}) - .def_static("implies", [](Expression const& lhs, Expression const& rhs) {return storm::expressions::implies(lhs, rhs);}) - .def_static("iff", [](Expression const& lhs, Expression const& rhs) {return storm::expressions::iff(lhs, rhs);}) + .def_static("Plus", [](Expression const& lhs, Expression const& rhs) {return lhs + rhs;}) + .def_static("Minus", [](Expression const& lhs, Expression const& rhs) {return lhs - rhs;}) + .def_static("Multiply", [](Expression const& lhs, Expression const& rhs) {return lhs * rhs;}) + .def_static("And", [](Expression const& lhs, Expression const& rhs) {return lhs && rhs;}) + .def_static("Or", [](Expression const& lhs, Expression const& rhs) {return lhs || rhs;}) + .def_static("Geq", [](Expression const& lhs, Expression const& rhs) {return lhs >= rhs;}) + .def_static("Eq", [](Expression const& lhs, Expression const& rhs) {return lhs == rhs;}) + .def_static("Neq", [](Expression const& lhs, Expression const& rhs) {return lhs != rhs;}) + .def_static("Greater", [](Expression const& lhs, Expression const& rhs) {return lhs > rhs;}) + .def_static("Less", [](Expression const& lhs, Expression const& rhs) {return lhs < rhs;}) + .def_static("Leq", [](Expression const& lhs, Expression const& rhs) {return lhs <= rhs;}) + .def_static("Implies", [](Expression const& lhs, Expression const& rhs) {return storm::expressions::implies(lhs, rhs);}) + .def_static("Iff", [](Expression const& lhs, Expression const& rhs) {return storm::expressions::iff(lhs, rhs);}) ; py::class_(m, "ExpressionParser", "Parser for storm-expressions") From cec2861a5d1c68941fef0b701ec5ca2cb03f5116 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 12:55:55 +0100 Subject: [PATCH 119/147] several extensions and fixes for jani data structures --- src/storage/choiceorigins.cpp | 6 +++-- src/storage/jani.cpp | 42 ++++++++++++++++++++++++----------- tests/storage/test_jani.py | 0 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 tests/storage/test_jani.py diff --git a/src/storage/choiceorigins.cpp b/src/storage/choiceorigins.cpp index c41cfe2..01488b6 100644 --- a/src/storage/choiceorigins.cpp +++ b/src/storage/choiceorigins.cpp @@ -5,9 +5,11 @@ void define_origins(py::module& m) { using ChoiceOrigins = storm::storage::sparse::ChoiceOrigins; py::class_> co(m, "ChoiceOrigins", "This class represents the origin of choices of a model in terms of the input model spec."); + co.def("get_identifier_info", &ChoiceOrigins::getIdentifierInfo, "identifier"_a, "human readable string") + .def("get_choice_info", &ChoiceOrigins::getChoiceInfo, "identifier"_a, "human readable string") ; using JaniChoiceOrigins = storm::storage::sparse::JaniChoiceOrigins; - py::class_>(m, "JaniChoiceOrigins", "This class represents for each choice the origin in the jani spec.") + py::class_>(m, "JaniChoiceOrigins", "This class represents for each choice the origin in the jani spec.", co) .def_property_readonly("model", &JaniChoiceOrigins::getModel, "retrieves the associated JANI model") - .def("get_edge_index_set", [](JaniChoiceOrigins const& co, uint64_t choice) { return co.getEdgeIndexSet(choice); }, "returns the set of edges that induced the choice", py::arg("choice_index")) + .def("get_edge_index_set", [](JaniChoiceOrigins const& co, uint64_t choice) -> auto& { return co.getEdgeIndexSet(choice); }, "returns the set of edges that induced the choice", py::arg("choice_index")) ; } \ No newline at end of file diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp index e40076a..9ce9771 100644 --- a/src/storage/jani.cpp +++ b/src/storage/jani.cpp @@ -16,31 +16,40 @@ std::string janiToString(Model const& m) { void define_jani(py::module& m) { py::class_> md(m, "JaniModel", "A Jani Model"); - md.def_property_readonly("name", &Model::getName, "model name") + md.def(py::init(), "other_model"_a) + .def_property_readonly("name", &Model::getName, "model name") .def_property_readonly("model_type", &storm::jani::Model::getModelType, "Model type") .def("set_model_type", &Model::setModelType, "Sets (only) the model type") .def_property_readonly("automata", [](const Model& model) -> auto& {return model.getAutomata();}, "get automata") + .def_property_readonly("global_variables", [](const Model& model) -> auto& {return model.getGlobalVariables();}) .def_property_readonly("constants", [](const Model& model) -> auto& {return model.getConstants();}, "get constants") + .def("get_constant", &Model::getConstant, "name"_a, "get constant by name") .def("restrict_edges", &Model::restrictEdges, "restrict model to edges given by set", py::arg("edge_set")) .def_property_readonly("expression_manager", &Model::getExpressionManager, "get expression manager", pybind11::return_value_policy::reference_internal) .def_property_readonly("has_undefined_constants", &Model::hasUndefinedConstants, "Flag if program has undefined constants") .def_property_readonly("undefined_constants_are_graph_preserving", &Model::undefinedConstantsAreGraphPreserving, "Flag if the undefined constants do not change the graph structure") .def("__str__", &janiToString) + .def_property("initial_states_restriction", &Model::getInitialStatesRestriction, &Model::setInitialStatesRestriction, "initial states restriction") .def("define_constants", &Model::defineUndefinedConstants, "define constants with a mapping from the corresponding expression variables to expressions", py::arg("map")) .def("substitute_constants", &Model::substituteConstants, "substitute constants") .def("remove_constant", &Model::removeConstant, "remove a constant. Make sure the constant does not appear in the model.", "constant_name"_a) .def("get_automaton_index", &Model::getAutomatonIndex, "name"_a, "get index for automaton name") + .def("add_automaton", &Model::addAutomaton, "automaton"_a, "add an automaton (with a unique name)") + .def("set_standard_system_composition", &Model::setStandardSystemComposition, "sets the composition to the standard composition") .def("replace_automaton", &Model::replaceAutomaton, "index"_a, "new_automaton"_a, "replace automaton at index") .def("check_valid", &Model::checkValid, "Some basic checks to ensure validity") - .def_static("encode_edge_and_automaton_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") - .def_static("decode_edge_and_automaton_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") + .def("substitute_functions", [](Model& model) {model.substituteFunctions();}, "substitute functions") + .def_static("encode_automaton_and_edge_index", &Model::encodeAutomatonAndEdgeIndices, "get edge/automaton-index") + .def_static("decode_automaton_and_edge_index", &Model::decodeAutomatonAndEdgeIndices, "get edge and automaton from edge/automaton index") + .def("make_standard_compliant", &Model::makeStandardJaniCompliant, "make standard JANI compliant") + .def("has_standard_composition", &Model::hasStandardComposition, "is the composition the standard composition") .def("finalize", &Model::finalize,"finalizes the model. After this action, be careful changing the data structure.") .def("to_dot", [](Model& model) {std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }) ; py::class_> automaton(m, "JaniAutomaton", "A Jani Automation"); automaton.def(py::init()) - .def_property_readonly("edges",[](const Automaton& a) { + .def_property_readonly("edges",[](const Automaton& a) -> auto& { return a.getEdges(); }, "get edges") .def_property_readonly("name", &Automaton::getName) @@ -59,7 +68,7 @@ void define_jani(py::module& m) { edge.def(py::init, std::shared_ptr, std::vector>>(), "source_location_index"_a, "action_index"_a, "rate"_a, "template_edge"_a, "destinations_with_probabilities"_a) .def_property_readonly("source_location_index", &Edge::getSourceLocationIndex, "index for source location") - .def_property_readonly("destinations", [](Edge const& e) { + .def_property_readonly("destinations", [](Edge const& e) -> auto& { return e.getDestinations(); }, "edge destinations") .def_property_readonly("action_index", &Edge::getActionIndex, "action index") @@ -67,15 +76,16 @@ void define_jani(py::module& m) { .def_property_readonly("rate", &Edge::getOptionalRate, "edge rate") .def_property_readonly("nr_destinations", &Edge::getNumberOfDestinations, "nr edge destinations") .def_property_readonly("guard", &Edge::getGuard, "edge guard") + .def_property("color", &Edge::getColor, &Edge::setColor, "color for the edge") .def("substitute", &Edge::substitute, py::arg("mapping")) .def("has_silent_action", &Edge::hasSilentAction, "Is the edge labelled with the silent action") ; py::class_> templateEdge(m, "JaniTemplateEdge", "Template edge, internal data structure for edges"); templateEdge.def(py::init()) - .def_property_readonly("assignments", [](TemplateEdge& te) { return te.getAssignments(); }) + .def_property_readonly("assignments", [](TemplateEdge& te) -> auto& { return te.getAssignments(); }) .def_property("guard", &TemplateEdge::getGuard, &TemplateEdge::setGuard) - .def_property_readonly("destinations",[](TemplateEdge& te) { return te.getDestinations(); }) + .def_property_readonly("destinations",[](TemplateEdge& te) -> auto& { return te.getDestinations(); }) .def("add_destination", &TemplateEdge::addDestination) ; @@ -87,7 +97,7 @@ void define_jani(py::module& m) { py::class_> templateEdgeDestination(m, "JaniTemplateEdgeDestination", "Template edge destination, internal data structure for edge destinations"); templateEdgeDestination.def(py::init(), "ordered_assignments"_a) - .def_property_readonly("assignments", [](TemplateEdgeDestination& ted) {return ted.getOrderedAssignments();}); + .def_property_readonly("assignments", [](TemplateEdgeDestination& ted) -> auto& {return ted.getOrderedAssignments();}); py::class_> orderedAssignments(m, "JaniOrderedAssignments", "Set of assignments"); orderedAssignments.def("__iter__", [](OrderedAssignments &v) { @@ -95,11 +105,13 @@ void define_jani(py::module& m) { }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ .def("__str__", &streamToString) .def("clone", &OrderedAssignments::clone, "clone assignments (performs a deep copy)") + .def("substitute", &OrderedAssignments::substitute, "substitute in rhs according to given substitution map", "substitution_map"_a) .def("add", [](OrderedAssignments& oa, Assignment const& newAssignment, bool addToExisting) {return oa.add(newAssignment, addToExisting); }, "new_assignment"_a, "add_to_existing"_a = false) ; py::class_> assignment(m, "JaniAssignment", "Jani Assignment"); - assignment.def("__str__", &streamToString) + assignment.def(py::init(), "lhs"_a, "rhs"_a, "lvl"_a = 0) + .def("__str__", &streamToString) .def_property("expression", &Assignment::getAssignedExpression, &Assignment::setAssignedExpression) ; @@ -113,9 +125,11 @@ void define_jani(py::module& m) { .def("__iter__", [](VariableSet &v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>()) - .def("add_variable", [](VariableSet& vs, Variable& v) { vs.addVariable(v); }) - .def("add_bounded_integer_variable", [](VariableSet& vs, BoundedIntegerVariable& v) { return vs.addVariable(v);}, "variable"_a) + .def("add_variable", [](VariableSet& vs, Variable& v) -> void { vs.addVariable(v); }) + .def("add_bounded_integer_variable", [](VariableSet& vs, BoundedIntegerVariable& v) -> auto& { return vs.addVariable(v); /*return vs.getVariable(v.getExpressionVariable());*/ }, py::return_value_policy::reference_internal, "variable"_a) .def("empty", &VariableSet::empty, "is there a variable in the set?") + .def("get_variable_by_name", [](VariableSet& v, std::string const& name) -> auto& { return v.getVariable(name);}) + .def("get_variable_by_expr_variable", [](VariableSet& v, storm::expressions::Variable const& var) -> auto& { return v.getVariable(var);}) ; py::class_> variable(m, "JaniVariable", "A Variable in JANI"); @@ -123,9 +137,11 @@ void define_jani(py::module& m) { .def_property_readonly("expression_variable", &Variable::getExpressionVariable, "expression variable for this variable") ; - py::class_ bivariable(m, "JaniBoundedIntegerVariable", "A Bounded Integer", variable); + py::class_> bivariable(m, "JaniBoundedIntegerVariable", "A Bounded Integer", variable); bivariable.def(py::init(), - "name"_a, "expression_variable"_a, "init_value"_a, "lower_bound"_a, "upper_bound"_a); + "name"_a, "expression_variable"_a, "init_value"_a, "lower_bound"_a, "upper_bound"_a) + .def(py::init(), + "name"_a, "expression_variable"_a, "lower_bound"_a, "upper_bound"_a);; py::class_> constant(m, "JaniConstant", "A Constant in JANI"); constant.def_property_readonly("defined", &Constant::isDefined, "is constant defined by some expression") diff --git a/tests/storage/test_jani.py b/tests/storage/test_jani.py new file mode 100644 index 0000000..e69de29 From 2e1f60a3c0c2e59f5470434cb2e794a24b740685 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 12:56:55 +0100 Subject: [PATCH 120/147] support for submodel construction --- lib/stormpy/__init__.py | 16 +++++++++++++++- src/core/transformation.cpp | 26 ++++++++++++++++++++++++++ src/storage/scheduler.cpp | 1 + 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index b3caa25..91607e2 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -411,4 +411,18 @@ def topological_sort(model, forward=True, initial=[]): elif isinstance(model, storage._SparseModel): return storage._topological_sort_double(matrix, initial) else: - raise StormError("Unknown kind of model.") \ No newline at end of file + raise StormError("Unknown kind of model.") + +def construct_submodel(model, states, actions, keep_unreachable_states = True, options = SubsystemBuilderOptions()): + """ + + :param model: The model + :param states: Which states should be preserved + :param actions: Which actions should be preserved + :param keep_unreachable_states: If False, run a reachability analysis. + :return: A model with fewer states/actions + """ + if isinstance(model, storage._SparseModel): + return core._construct_subsystem_double(model, states, actions, keep_unreachable_states, options) + else: + raise NotImplementedError() \ No newline at end of file diff --git a/src/core/transformation.cpp b/src/core/transformation.cpp index bdf939c..59541cf 100644 --- a/src/core/transformation.cpp +++ b/src/core/transformation.cpp @@ -1,8 +1,34 @@ #include "transformation.h" #include "storm/models/symbolic/StandardRewardModel.h" +#include "storm/transformer/SubsystemBuilder.h" + +// Thin wrappers. +template +storm::transformer::SubsystemBuilderReturnType constructSubsystem(storm::models::sparse::Model const& originalModel, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& subsystemActions, bool keepUnreachableStates, storm::transformer::SubsystemBuilderOptions options) { + return storm::transformer::buildSubsystem(originalModel, subsystemStates, subsystemActions, keepUnreachableStates, options); +} void define_transformation(py::module& m) { // Transform model m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model"), py::arg("formulae") = std::vector>()); m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model"), py::arg("formulae") = std::vector>()); + + py::class_>(m, "SubsystemBuilderReturnTypeDouble", "Result of the construction of a subsystem") + .def_readonly("model", &storm::transformer::SubsystemBuilderReturnType::model, "the submodel") + .def_readonly("new_to_old_state_mapping", &storm::transformer::SubsystemBuilderReturnType::newToOldStateIndexMapping, "for each state in result, the state index in the original model") + .def_readonly("new_to_old_action_mapping", &storm::transformer::SubsystemBuilderReturnType::newToOldActionIndexMapping, "for each action in result, the action index in the original model") + .def_readonly("kept_actions", &storm::transformer::SubsystemBuilderReturnType::keptActions, "Actions of the subsystem available in the original system") + ; + + py::class_(m, "SubsystemBuilderOptions", "Options for constructing the subsystem") + .def(py::init<>()) + .def_readwrite("check_transitions_outside", &storm::transformer::SubsystemBuilderOptions::checkTransitionsOutside) + .def_readwrite("build_state_mapping", &storm::transformer::SubsystemBuilderOptions::buildStateMapping) + .def_readwrite("build_action_mapping", &storm::transformer::SubsystemBuilderOptions::buildActionMapping) + .def_readwrite("build_kept_actions", &storm::transformer::SubsystemBuilderOptions::buildKeptActions) + ; + + + m.def("_construct_subsystem_double", &constructSubsystem, "build a subsystem of a sparse model"); + } \ No newline at end of file diff --git a/src/storage/scheduler.cpp b/src/storage/scheduler.cpp index 5d79f09..a553c83 100644 --- a/src/storage/scheduler.cpp +++ b/src/storage/scheduler.cpp @@ -20,6 +20,7 @@ void define_scheduler(py::module& m, std::string vt_suffix) { .def_property_readonly("memory_size", &Scheduler::getNumberOfMemoryStates, "How much memory does the scheduler take?") .def_property_readonly("deterministic", &Scheduler::isDeterministicScheduler, "Is the scheduler deterministic?") .def("get_choice", &Scheduler::getChoice, py::arg("state_index"), py::arg("memory_index") = 0) + .def("compute_action_support", &Scheduler::computeActionSupport, "nondeterministic_choice_indices"_a) ; std::string schedulerChoiceClassName = std::string("SchedulerChoice") + vt_suffix; From 9317d540838e1257c4708642dd10aef5ae560709 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Thu, 29 Nov 2018 12:59:58 +0100 Subject: [PATCH 121/147] extended support for mdps --- src/storage/model.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/storage/model.cpp b/src/storage/model.cpp index 45594d6..1170dcc 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -16,6 +16,8 @@ #include "storm/models/symbolic/MarkovAutomaton.h" #include "storm/models/symbolic/StandardRewardModel.h" +#include "storm/storage/Scheduler.h" + #include #include #include @@ -105,6 +107,7 @@ void define_model(py::module& m) { py::class_> modelBase(m, "_ModelBase", "Base class for all models"); modelBase.def_property_readonly("nr_states", &ModelBase::getNumberOfStates, "Number of states") .def_property_readonly("nr_transitions", &ModelBase::getNumberOfTransitions, "Number of transitions") + .def_property_readonly("nr_choices", &ModelBase::getNumberOfChoices, "Number of choices") .def_property_readonly("model_type", &ModelBase::getType, "Model type") .def_property_readonly("supports_parameters", &ModelBase::supportsParameters, "Flag whether model supports parameters") .def_property_readonly("has_parameters", &ModelBase::hasParameters, "Flag whether model has parameters") @@ -183,11 +186,14 @@ void define_sparse_model(py::module& m) { .def_property_readonly("backward_transition_matrix", &SparseModel::getBackwardTransitions, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix") .def("reduce_to_state_based_rewards", &SparseModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter()) + .def("to_dot", [](SparseModel& model) { std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }, "Write dot to a string") ; py::class_, std::shared_ptr>>(m, "SparseDtmc", "DTMC in sparse representation", model) .def("__str__", getModelInfoPrinter("DTMC")) ; py::class_, std::shared_ptr>>(m, "SparseMdp", "MDP in sparse representation", model) + .def_property_readonly("nondeterministic_choice_indices", [](SparseMdp const& mdp) { return mdp.getNondeterministicChoiceIndices(); }) + .def("apply_scheduler", [](SparseMdp const& mdp, storm::storage::Scheduler const& scheduler, bool dropUnreachableStates) { return mdp.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true) .def("__str__", getModelInfoPrinter("MDP")) ; py::class_, std::shared_ptr>>(m, "SparsePomdp", "POMDP in sparse representation", model) @@ -209,6 +215,7 @@ void define_sparse_model(py::module& m) { .def_property_readonly("transition_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getTransitionRewardMatrix();}) .def_property_readonly("state_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateRewardVector();}) .def("get_state_reward", [](SparseRewardModel& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);}) + .def("get_zero_reward_states", &SparseRewardModel::getStatesWithZeroReward, "get states where all rewards are zero", py::arg("transition_matrix")) .def("get_state_action_reward", [](SparseRewardModel& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);}) .def_property_readonly("state_action_rewards", [](SparseRewardModel& rewardModel) {return rewardModel.getStateActionRewardVector();}) .def("reduce_to_state_based_rewards", [](SparseRewardModel& rewardModel, storm::storage::SparseMatrix const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);}, py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards") @@ -237,6 +244,8 @@ void define_sparse_model(py::module& m) { .def("__str__", getModelInfoPrinter("ParametricDTMC")) ; py::class_, std::shared_ptr>>(m, "SparseParametricMdp", "pMDP in sparse representation", modelRatFunc) + .def_property_readonly("nondeterministic_choice_indices", [](SparseMdp const& mdp) { return mdp.getNondeterministicChoiceIndices(); }) + .def("apply_scheduler", [](SparseMdp const& mdp, storm::storage::Scheduler const& scheduler, bool dropUnreachableStates) { return mdp.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true) .def("__str__", getModelInfoPrinter("ParametricMDP")) ; py::class_, std::shared_ptr>>(m, "SparseParametricCtmc", "pCTMC in sparse representation", modelRatFunc) From af5872d6336125bb84f857fd717c91c864d6a7a4 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 6 Dec 2018 18:48:19 +0100 Subject: [PATCH 122/147] Fixed conversion to raw_formula for properties --- lib/stormpy/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 91607e2..6f5a652 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -125,7 +125,7 @@ def build_sparse_model(symbolic_description, properties=None): raise StormError("Program still contains undefined constants") if properties: - formulae = [prop.raw_formula for prop in properties] + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] intermediate = core._build_sparse_model_from_symbolic_description(symbolic_description, formulae) else: intermediate = core._build_sparse_model_from_symbolic_description(symbolic_description) @@ -144,7 +144,7 @@ def build_sparse_parametric_model(symbolic_description, properties=None): raise StormError("Program still contains undefined constants") if properties: - formulae = [prop.raw_formula for prop in properties] + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] intermediate = core._build_sparse_parametric_model_from_symbolic_description(symbolic_description, formulae) else: intermediate = core._build_sparse_parametric_model_from_symbolic_description(symbolic_description) @@ -182,7 +182,7 @@ def build_symbolic_parametric_model(symbolic_description, properties=None): raise StormError("Program still contains undefined constants") if properties: - formulae = [(prop.raw_formula if isinstance(property, Property) else prop) for prop in properties] + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description, formulae) else: intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description) @@ -230,7 +230,7 @@ def perform_sparse_bisimulation(model, properties, bisimulation_type): :param bisimulation_type: Type of bisimulation (weak or strong). :return: Model after bisimulation. """ - formulae = [prop.raw_formula for prop in properties] + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] if model.supports_parameters: return core._perform_parametric_bisimulation(model, formulae, bisimulation_type) else: @@ -244,7 +244,7 @@ def perform_symbolic_bisimulation(model, properties): :param properties: Properties to preserve during bisimulation. :return: Model after bisimulation. """ - formulae = [prop.raw_formula for prop in properties] + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] bisimulation_type = BisimulationType.STRONG if model.supports_parameters: return core._perform_symbolic_parametric_bisimulation(model, formulae, bisimulation_type) @@ -425,4 +425,4 @@ def construct_submodel(model, states, actions, keep_unreachable_states = True, o if isinstance(model, storage._SparseModel): return core._construct_subsystem_double(model, states, actions, keep_unreachable_states, options) else: - raise NotImplementedError() \ No newline at end of file + raise NotImplementedError() From 98e61814f8f0fd18ac88f219c19d2cbd000c3878 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 7 Dec 2018 13:12:02 +0100 Subject: [PATCH 123/147] fix test to use new capitalised operators --- tests/utility/test_smtsolver.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/utility/test_smtsolver.py b/tests/utility/test_smtsolver.py index 7655f3a..39fb265 100644 --- a/tests/utility/test_smtsolver.py +++ b/tests/utility/test_smtsolver.py @@ -22,8 +22,8 @@ class TestSmtSolver(): manager = stormpy.ExpressionManager() x = manager.create_integer_variable("x") xe = x.get_expression() - c1 = stormpy.Expression.geq(xe, manager.create_integer(1)) - c2 = stormpy.Expression.less(xe, manager.create_integer(0)) + c1 = stormpy.Expression.Geq(xe, manager.create_integer(1)) + c2 = stormpy.Expression.Less(xe, manager.create_integer(0)) solver = stormpy.utility.Z3SmtSolver(manager) solver.add(c1) solver.add(c2) @@ -33,8 +33,8 @@ class TestSmtSolver(): manager = stormpy.ExpressionManager() x = manager.create_integer_variable("x") xe = x.get_expression() - c1 = stormpy.Expression.geq(xe, manager.create_integer(1)) - c2 = stormpy.Expression.less(xe, manager.create_integer(0)) + c1 = stormpy.Expression.Geq(xe, manager.create_integer(1)) + c2 = stormpy.Expression.Less(xe, manager.create_integer(0)) solver = stormpy.utility.Z3SmtSolver(manager) solver.add(c1) solver.add(c2) @@ -44,8 +44,8 @@ class TestSmtSolver(): manager = stormpy.ExpressionManager() x = manager.create_integer_variable("x") xe = x.get_expression() - c1 = stormpy.Expression.geq(xe, manager.create_integer(1)) - c2 = stormpy.Expression.less(xe, manager.create_integer(2)) + c1 = stormpy.Expression.Geq(xe, manager.create_integer(1)) + c2 = stormpy.Expression.Less(xe, manager.create_integer(2)) solver = stormpy.utility.Z3SmtSolver(manager) solver.add(c1) solver.add(c2) From 910e24a73e679253cc38251c22132ca04c932822 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 7 Dec 2018 14:04:29 +0100 Subject: [PATCH 124/147] fixed tests based on changes in storm --- tests/storage/test_model.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/storage/test_model.py b/tests/storage/test_model.py index 532f88c..bca8d35 100644 --- a/tests/storage/test_model.py +++ b/tests/storage/test_model.py @@ -99,12 +99,18 @@ class TestSparseModel: def test_build_ctmc(self): program = stormpy.parse_prism_program(get_example_path("ctmc", "polling2.sm"), True) formulas = stormpy.parse_properties_for_prism_program("P=? [ F<=3 \"target\" ]", program) - model = stormpy.build_model(program, formulas) + model = stormpy.build_model(program) assert model.nr_states == 12 assert model.nr_transitions == 22 assert model.model_type == stormpy.ModelType.CTMC assert not model.supports_parameters assert type(model) is stormpy.SparseCtmc + model_for_formula = stormpy.build_model(program, formulas) + assert model_for_formula.nr_states == 1 + assert model_for_formula.nr_transitions == 1 + assert model_for_formula.model_type == stormpy.ModelType.CTMC + assert not model_for_formula.supports_parameters + assert type(model_for_formula) is stormpy.SparseCtmc def test_build_pomdp(self): program = stormpy.parse_prism_program(get_example_path("pomdp", "maze_2.prism")) @@ -115,11 +121,11 @@ class TestSparseModel: def test_build_ma(self): - program = stormpy.parse_prism_program(get_example_path("ma", "simple.ma")) - formulas = stormpy.parse_properties_for_prism_program("P=? [ F<=2 s=2 ]", program) + program = stormpy.parse_prism_program(get_example_path("ma", "simple.ma"), False, True) + formulas = stormpy.parse_properties_for_prism_program("Pmax=? [ F<=2 s=2 ]", program) model = stormpy.build_model(program, formulas) - assert model.nr_states == 5 - assert model.nr_transitions == 8 + assert model.nr_states == 4 + assert model.nr_transitions == 7 assert model.model_type == stormpy.ModelType.MA assert not model.supports_parameters assert type(model) is stormpy.SparseMA From bdff55242edc9253a38cb240f2105c9ac6ec4399 Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 7 Dec 2018 14:12:47 +0100 Subject: [PATCH 125/147] setting stormpy version to 1.3.0 in preparation of release --- lib/stormpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stormpy/_version.py b/lib/stormpy/_version.py index c68196d..67bc602 100644 --- a/lib/stormpy/_version.py +++ b/lib/stormpy/_version.py @@ -1 +1 @@ -__version__ = "1.2.0" +__version__ = "1.3.0" From aaf22508a1d0d8352293dbadf87b1bc9e8f28bef Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 7 Dec 2018 14:12:59 +0100 Subject: [PATCH 126/147] update changelog in preparation of release --- CHANGELOG.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e51bc5..7616f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,27 @@ Changelog ============= - -Version 1.2.x +Version 1.3.x ------------- -### Version 1.2.x -Requires storm version >= 1.2.2 and pycarl version >= 2.0.2 +### Version 1.3.0 (2018/12) +Requires storm version >= 1.3.0 and pycarl version >= 2.0.2 - Adaptions to changes in Storm - Bindings for symbolic models: * building symbolic models * bisimulation * transforming symbolic to sparse models - Extraction of schedulers and queries on schedulers +- High-level counterexamples connected +- Drastically extended JANI bindings +- Extended bindings for expressions - Extended PLA bindings - Extended DFT bindings - Extended documentation +Version 1.2.x +------------- + ### Version 1.2.0 Requires storm version >= 1.2.0 and pycarl version >= 2.0.2 - Adaptions to changes in Storm From 7c8110f38d2bc8e6971f6d6aded921b30f234adb Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 7 Dec 2018 15:05:12 +0100 Subject: [PATCH 127/147] extra example file listed --- lib/stormpy/examples/files.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/stormpy/examples/files.py b/lib/stormpy/examples/files.py index 748b419..5e7a447 100644 --- a/lib/stormpy/examples/files.py +++ b/lib/stormpy/examples/files.py @@ -27,6 +27,8 @@ jani_dtmc_die = _path("dtmc", "die.jani") """Jani Version of Knuth Yao Die Example""" prism_mdp_coin_2_2 = _path("mdp", "coin2-2.nm") """Prism example for coin MDP""" +prism_pmdp_coin_two_dice = _path("pmdp", "two_dice.nm") +"""Prism example for parametric two dice""" prism_mdp_maze = _path("mdp", "maze_2.nm") """Prism example for the maze MDP""" prism_pomdp_maze = _path("pomdp", "maze_2.prism") From e35f390414b8d5131a23bdb008a8ae7964c22a9e Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 7 Dec 2018 15:06:40 +0100 Subject: [PATCH 128/147] copy sparse models --- src/storage/model.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/storage/model.cpp b/src/storage/model.cpp index 1170dcc..c94d954 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -189,23 +189,28 @@ void define_sparse_model(py::module& m) { .def("to_dot", [](SparseModel& model) { std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }, "Write dot to a string") ; py::class_, std::shared_ptr>>(m, "SparseDtmc", "DTMC in sparse representation", model) - .def("__str__", getModelInfoPrinter("DTMC")) + .def(py::init>(), py::arg("other_model")) + .def("__str__", getModelInfoPrinter("DTMC")) ; py::class_, std::shared_ptr>>(m, "SparseMdp", "MDP in sparse representation", model) + .def(py::init>(), py::arg("other_model")) .def_property_readonly("nondeterministic_choice_indices", [](SparseMdp const& mdp) { return mdp.getNondeterministicChoiceIndices(); }) .def("apply_scheduler", [](SparseMdp const& mdp, storm::storage::Scheduler const& scheduler, bool dropUnreachableStates) { return mdp.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true) .def("__str__", getModelInfoPrinter("MDP")) ; py::class_, std::shared_ptr>>(m, "SparsePomdp", "POMDP in sparse representation", model) + .def(py::init>(), py::arg("other_model")) .def("__str__", getModelInfoPrinter("POMDP")) .def_property_readonly("observations", &SparsePomdp::getObservations) .def_property_readonly("nr_observations", &SparsePomdp::getNrObservations) ; py::class_, std::shared_ptr>>(m, "SparseCtmc", "CTMC in sparse representation", model) - .def("__str__", getModelInfoPrinter("CTMC")) + .def(py::init>(), py::arg("other_model")) + .def("__str__", getModelInfoPrinter("CTMC")) ; py::class_, std::shared_ptr>>(m, "SparseMA", "MA in sparse representation", model) - .def("__str__", getModelInfoPrinter("MA")) + .def(py::init>(), py::arg("other_model")) + .def("__str__", getModelInfoPrinter("MA")) ; py::class_>(m, "SparseRewardModel", "Reward structure for sparse models") From 5eb7af76b135fc3a89a75cd265f4b3528b2c0122 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 11 Dec 2018 13:42:37 +0100 Subject: [PATCH 129/147] Travis: use new Storm release 1.3.0 --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91d6a1d..d4133ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,13 +40,13 @@ jobs: # Docker Storm stable - os: linux compiler: gcc - env: TASK=Test CONFIG=Release DOCKER=storm:1.2.3 PYTHON=python3 + env: TASK=Test CONFIG=Release DOCKER=storm:1.3.0 PYTHON=python3 script: travis/build.sh # Docker Storm stable in debug mode - os: linux compiler: gcc - env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.3-debug PYTHON=python3 + env: TASK=Test CONFIG=Debug DOCKER=storm:1.3.0-debug PYTHON=python3 script: travis/build.sh # Documentation @@ -68,6 +68,6 @@ jobs: # Allow failures of stable versions as new features might have been added allow_failures: - os: linux - env: TASK=Test CONFIG=Release DOCKER=storm:1.2.3 PYTHON=python3 + env: TASK=Test CONFIG=Release DOCKER=storm:1.3.0 PYTHON=python3 - os: linux - env: TASK=Test CONFIG=Debug DOCKER=storm:1.2.3-debug PYTHON=python3 + env: TASK=Test CONFIG=Debug DOCKER=storm:1.3.0-debug PYTHON=python3 From abe5c8d69ea26486ec51e18d294ee03ad91e36a5 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 11 Dec 2018 15:08:18 +0100 Subject: [PATCH 130/147] Require Storm version >= 1.3.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 48068e8..8b30314 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ if sys.version_info[0] == 2: sys.exit('Sorry, Python 2.x is not supported') # Minimal storm version required -storm_min_version = "1.2.4" +storm_min_version = "1.3.0" # Get the long description from the README file with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md'), encoding='utf-8') as f: From b30cae0d6d52ee66f9f0f5b5b1a9791f9d1af978 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 2 Jan 2019 17:54:29 +0100 Subject: [PATCH 131/147] Updated CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7616f40..ce1c4a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog Version 1.3.x ------------- -### Version 1.3.0 (2018/12) +### Version 1.3.0 (2019/01) Requires storm version >= 1.3.0 and pycarl version >= 2.0.2 - Adaptions to changes in Storm - Bindings for symbolic models: @@ -18,6 +18,7 @@ Requires storm version >= 1.3.0 and pycarl version >= 2.0.2 - Extended PLA bindings - Extended DFT bindings - Extended documentation +- Improved and extended setup Version 1.2.x ------------- From 7268fc14720dab558eb12124cfe55d01a7523449 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 2 Jan 2019 17:54:52 +0100 Subject: [PATCH 132/147] Set version in doc automatically --- doc/source/conf.py | 6 +++--- lib/stormpy/__init__.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index c1a41d1..0c71a5e 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -56,7 +56,7 @@ master_doc = 'index' # General information about the project. project = 'stormpy' -copyright = '2016--2018 Moves RWTH Aachen' +copyright = '2016-2019 Moves RWTH Aachen' author = 'Sebastian Junges, Matthias Volk' # The version info for the project you're documenting, acts as replacement for @@ -64,9 +64,9 @@ author = 'Sebastian Junges, Matthias Volk' # built documents. # # The short X.Y version. -version = '' +version = stormpy.__version__ # The full version, including alpha/beta/rc tags. -release = '' +release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index 6f5a652..bf19d5a 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -15,7 +15,7 @@ from pycarl import Variable # needed for building parametric models __version__ = "unknown" try: - from _version import __version__ + from ._version import __version__ except ImportError: # We're running in a tree that doesn't have a _version.py, so we don't know what our version is. pass From 57f4dfc344f5e2482537a9cefb87365443b266cf Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 2 Jan 2019 17:57:33 +0100 Subject: [PATCH 133/147] Require newest pycarl version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8b30314..c18ee19 100755 --- a/setup.py +++ b/setup.py @@ -234,7 +234,7 @@ setup( cmdclass={'build_ext': CMakeBuild}, zip_safe=False, - install_requires=['pycarl>=2.0.2'], + install_requires=['pycarl>=2.0.3'], setup_requires=['pytest-runner'], tests_require=['pytest'], python_requires='>=3', From 6233fc5f820cd7ab18cab8085ce3f51b2a112a96 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 2 Jan 2019 18:57:16 +0100 Subject: [PATCH 134/147] Set correct pycarl version in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce1c4a0..347b676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Version 1.3.x ------------- ### Version 1.3.0 (2019/01) -Requires storm version >= 1.3.0 and pycarl version >= 2.0.2 +Requires storm version >= 1.3.0 and pycarl version >= 2.0.3 - Adaptions to changes in Storm - Bindings for symbolic models: * building symbolic models From 333804b2084afb44ee0f2d18c0ed9346ccbd91b4 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 29 Jan 2019 21:14:34 +0100 Subject: [PATCH 135/147] Transformation from CTMCs to DTMCs --- lib/stormpy/__init__.py | 19 ++++++++++++++++++- src/core/transformation.cpp | 9 +++++++++ tests/core/test_transformation.py | 24 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py index bf19d5a..368fbcb 100644 --- a/lib/stormpy/__init__.py +++ b/lib/stormpy/__init__.py @@ -334,6 +334,7 @@ def check_model_hybrid(model, property, only_initial_states=False, environment=E task = core.CheckTask(formula, only_initial_states) return core._model_checking_hybrid_engine(model, task, environment=environment) + def transform_to_sparse_model(model): """ Transform model in symbolic representation into model in sparse representation. @@ -346,6 +347,20 @@ def transform_to_sparse_model(model): return core._transform_to_sparse_model(model) +def transform_to_discrete_time_model(model, properties): + """ + Transform continuous-time model to discrete time model. + :param model: Continuous-time model. + :param properties: List of properties to transform as well. + :return: Tuple (Discrete-time model, converted properties). + """ + formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties] + if model.supports_parameters: + return core._transform_to_discrete_time_parametric_model(model, formulae) + else: + return core._transform_to_discrete_time_model(model, formulae) + + def prob01min_states(model, eventually_formula): assert type(eventually_formula) == logic.EventuallyFormula labelform = eventually_formula.subformula @@ -398,6 +413,7 @@ def compute_prob01max_states(model, phi_states, psi_states): else: return core._compute_prob01states_max_double(model, phi_states, psi_states) + def topological_sort(model, forward=True, initial=[]): """ @@ -413,7 +429,8 @@ def topological_sort(model, forward=True, initial=[]): else: raise StormError("Unknown kind of model.") -def construct_submodel(model, states, actions, keep_unreachable_states = True, options = SubsystemBuilderOptions()): + +def construct_submodel(model, states, actions, keep_unreachable_states=True, options=SubsystemBuilderOptions()): """ :param model: The model diff --git a/src/core/transformation.cpp b/src/core/transformation.cpp index 59541cf..a852563 100644 --- a/src/core/transformation.cpp +++ b/src/core/transformation.cpp @@ -1,4 +1,5 @@ #include "transformation.h" +#include "storm/api/transformation.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/transformer/SubsystemBuilder.h" @@ -8,11 +9,19 @@ storm::transformer::SubsystemBuilderReturnType constructSubsystem(storm::mod return storm::transformer::buildSubsystem(originalModel, subsystemStates, subsystemActions, keepUnreachableStates, options); } +template +std::pair>, std::vector>> transformContinuousToDiscreteTimeSparseModel(std::shared_ptr> const& model, std::vector> const& formulas) { + return storm::api::transformContinuousToDiscreteTimeSparseModel(model, formulas); +} + void define_transformation(py::module& m) { // Transform model m.def("_transform_to_sparse_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic model into sparse model", py::arg("model"), py::arg("formulae") = std::vector>()); m.def("_transform_to_sparse_parametric_model", &storm::api::transformSymbolicToSparseModel, "Transform symbolic parametric model into sparse parametric model", py::arg("model"), py::arg("formulae") = std::vector>()); + m.def("_transform_to_discrete_time_model", &transformContinuousToDiscreteTimeSparseModel, "Transform continuous time model to discrete time model", py::arg("model"), py::arg("formulae") = std::vector>()); + m.def("_transform_to_discrete_time_parametric_model", &transformContinuousToDiscreteTimeSparseModel, "Transform continuous time model to discrete time model", py::arg("model"), py::arg("formulae") = std::vector>()); + py::class_>(m, "SubsystemBuilderReturnTypeDouble", "Result of the construction of a subsystem") .def_readonly("model", &storm::transformer::SubsystemBuilderReturnType::model, "the submodel") .def_readonly("new_to_old_state_mapping", &storm::transformer::SubsystemBuilderReturnType::newToOldStateIndexMapping, "for each state in result, the state index in the original model") diff --git a/tests/core/test_transformation.py b/tests/core/test_transformation.py index e1a322e..803c57e 100644 --- a/tests/core/test_transformation.py +++ b/tests/core/test_transformation.py @@ -2,6 +2,8 @@ import stormpy import stormpy.logic from helpers.helper import get_example_path +import math + class TestTransformation: def test_transform_symbolic_dtmc_to_sparse(self): @@ -33,3 +35,25 @@ class TestTransformation: assert symbolic_model.model_type == stormpy.ModelType.DTMC assert symbolic_model.supports_parameters assert type(symbolic_model) is stormpy.SparseParametricDtmc + + def test_transform_continuous_to_discrete_time_model(self): + ctmc = stormpy.build_model_from_drn(get_example_path("ctmc", "dft.drn")) + formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") + print(formulas) + assert ctmc.nr_states == 16 + assert ctmc.nr_transitions == 33 + assert len(ctmc.initial_states) == 1 + initial_state = ctmc.initial_states[0] + assert initial_state == 1 + result = stormpy.model_checking(ctmc, formulas[0]) + assert math.isclose(result.at(initial_state), 4.166666667) + + dtmc, dtmc_formulas = stormpy.transform_to_discrete_time_model(ctmc, formulas) + print(dtmc_formulas) + assert dtmc.nr_states == 16 + assert dtmc.nr_transitions == 33 + assert len(dtmc.initial_states) == 1 + initial_state = dtmc.initial_states[0] + assert initial_state == 1 + result = stormpy.model_checking(dtmc, dtmc_formulas[0]) + assert math.isclose(result.at(initial_state), 4.166666667) From fdf566876eef1ba9886a942779f16fbba33a9e26 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 6 Feb 2019 15:14:20 +0100 Subject: [PATCH 136/147] Increment required Storm version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c18ee19..de75c00 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ if sys.version_info[0] == 2: sys.exit('Sorry, Python 2.x is not supported') # Minimal storm version required -storm_min_version = "1.3.0" +storm_min_version = "1.3.1" # Get the long description from the README file with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md'), encoding='utf-8') as f: From 2e218a14b36daf3aa04be8eba1f87091cd360e6f Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Wed, 6 Feb 2019 23:00:12 +0100 Subject: [PATCH 137/147] add instantiation checker (for pDTMCs) --- src/mod_pars.cpp | 1 + src/pars/model_instantiator.cpp | 35 +++++++++++++++++++++++++++++++++ src/pars/model_instantiator.h | 1 + 3 files changed, 37 insertions(+) diff --git a/src/mod_pars.cpp b/src/mod_pars.cpp index ee28960..665397c 100644 --- a/src/mod_pars.cpp +++ b/src/mod_pars.cpp @@ -15,4 +15,5 @@ PYBIND11_MODULE(pars, m) { define_pars(m); define_pla(m); define_model_instantiator(m); + define_model_instantiation_checker(m); } diff --git a/src/pars/model_instantiator.cpp b/src/pars/model_instantiator.cpp index 78b0376..6040230 100644 --- a/src/pars/model_instantiator.cpp +++ b/src/pars/model_instantiator.cpp @@ -1,5 +1,24 @@ #include "model_instantiator.h" #include "storm/models/sparse/Model.h" +#include "storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.h" +#include "storm/models/sparse/StandardRewardModel.h" + + +#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h" + +#include "storm/adapters/RationalFunctionAdapter.h" +#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" +#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h" +#include "storm/modelchecker/prctl/helper/BaierUpperRewardBoundsComputer.h" +#include "storm/models/sparse/Dtmc.h" +#include "storm/models/sparse/StandardRewardModel.h" +#include "storm/solver/MinMaxLinearEquationSolver.h" +#include "storm/solver/Multiplier.h" +#include "storm/utility/vector.h" +#include "storm/utility/graph.h" +#include "storm/utility/NumberTraits.h" template using Model = storm::models::sparse::Model; template using Dtmc = storm::models::sparse::Dtmc; @@ -7,6 +26,8 @@ template using Mdp = storm::models::sparse::Mdp; template using Ctmc = storm::models::sparse::Ctmc; template using MarkovAutomaton = storm::models::sparse::MarkovAutomaton; +using namespace storm::modelchecker; + // Model instantiator void define_model_instantiator(py::module& m) { py::class_, Dtmc>>(m, "PDtmcInstantiator", "Instantiate PDTMCs to DTMCs") @@ -27,3 +48,17 @@ void define_model_instantiator(py::module& m) { .def(py::init>(), "parametric model"_a) .def("instantiate", &storm::utility::ModelInstantiator, MarkovAutomaton>::instantiate, "Instantiate model with given parameter values"); } + +void define_model_instantiation_checker(py::module& m) { + + py::class_, double>, std::shared_ptr, double>>> bpdtmcinstchecker(m, "PDtmcInstantiationCheckerBase", "Instantiate pDTMCs to DTMCs (base)"); + bpdtmcinstchecker.def("specify_formula", &SparseInstantiationModelChecker, double>::specifyFormula, "check_task"_a) + ; + py::class_, double>, std::shared_ptr, double>>> (m, "PDtmcInstantiationChecker", "Instantiate pDTMCs to DTMCs", bpdtmcinstchecker) + + .def(py::init>(), "parametric model"_a) + .def("check", [](SparseDtmcInstantiationModelChecker, double> &sdimc, storm::Environment const& env, storm::utility::parametric::Valuation const& val) -> std::shared_ptr {return sdimc.check(env,val);}, "env"_a, "instantiation"_a) + .def("set_graph_preserving", &SparseDtmcInstantiationModelChecker, double>::setInstantiationsAreGraphPreserving, "value"_a); +; + +} \ No newline at end of file diff --git a/src/pars/model_instantiator.h b/src/pars/model_instantiator.h index d841821..52ab136 100644 --- a/src/pars/model_instantiator.h +++ b/src/pars/model_instantiator.h @@ -4,5 +4,6 @@ #include "common.h" void define_model_instantiator(py::module& m); +void define_model_instantiation_checker(py::module& m); #endif /* PYTHON_PARS_MODEL_INSTANTIATOR_H_ */ From e9ddc058f68509ad2cf71af07d1d41a7a213601a Mon Sep 17 00:00:00 2001 From: Sebastian Junges Date: Fri, 15 Feb 2019 12:43:24 +0100 Subject: [PATCH 138/147] instantiation checker for mdps --- src/pars/model_instantiator.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pars/model_instantiator.cpp b/src/pars/model_instantiator.cpp index 6040230..e13c0a7 100644 --- a/src/pars/model_instantiator.cpp +++ b/src/pars/model_instantiator.cpp @@ -51,14 +51,24 @@ void define_model_instantiator(py::module& m) { void define_model_instantiation_checker(py::module& m) { - py::class_, double>, std::shared_ptr, double>>> bpdtmcinstchecker(m, "PDtmcInstantiationCheckerBase", "Instantiate pDTMCs to DTMCs (base)"); + py::class_, double>, std::shared_ptr, double>>> bpdtmcinstchecker(m, "_PDtmcInstantiationCheckerBase", "Instantiate pDTMCs to DTMCs and immediately check (base)"); bpdtmcinstchecker.def("specify_formula", &SparseInstantiationModelChecker, double>::specifyFormula, "check_task"_a) ; - py::class_, double>, std::shared_ptr, double>>> (m, "PDtmcInstantiationChecker", "Instantiate pDTMCs to DTMCs", bpdtmcinstchecker) + py::class_, double>, std::shared_ptr, double>>> (m, "PDtmcInstantiationChecker", "Instantiate pDTMCs to DTMCs and immediately check", bpdtmcinstchecker) .def(py::init>(), "parametric model"_a) .def("check", [](SparseDtmcInstantiationModelChecker, double> &sdimc, storm::Environment const& env, storm::utility::parametric::Valuation const& val) -> std::shared_ptr {return sdimc.check(env,val);}, "env"_a, "instantiation"_a) .def("set_graph_preserving", &SparseDtmcInstantiationModelChecker, double>::setInstantiationsAreGraphPreserving, "value"_a); + + + py::class_, double>, std::shared_ptr, double>>> bpmdpinstchecker(m, "_PMdpInstantiationCheckerBase", "Instantiate pMDPs to MDPs and immediately check (base)"); + bpmdpinstchecker.def("specify_formula", &SparseInstantiationModelChecker, double>::specifyFormula, "check_task"_a) + ; + py::class_, double>, std::shared_ptr, double>>> (m, "PMdpInstantiationChecker", "Instantiate PMDP to MDPs and immediately check", bpmdpinstchecker) + + .def(py::init>(), "parametric model"_a) + .def("check", [](SparseMdpInstantiationModelChecker, double> &sdimc, storm::Environment const& env, storm::utility::parametric::Valuation const& val) -> std::shared_ptr {return sdimc.check(env,val);}, "env"_a, "instantiation"_a) + .def("set_graph_preserving", &SparseMdpInstantiationModelChecker, double>::setInstantiationsAreGraphPreserving, "value"_a); ; } \ No newline at end of file From 2908ac1b703907ccad7a6b1311ed39873a3542f0 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Tue, 19 Feb 2019 15:21:16 +0100 Subject: [PATCH 139/147] Get all parameters from sparse or symbolic model --- src/storage/model.cpp | 13 +++++++++++-- tests/pars/test_parametric.py | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/storage/model.cpp b/src/storage/model.cpp index c94d954..441b434 100644 --- a/src/storage/model.cpp +++ b/src/storage/model.cpp @@ -67,6 +67,15 @@ std::set rewardVariables(SparseModel allVariables(SparseModel const& model) { + return storm::models::sparse::getAllParameters(model); +} + + +/*std::set getParameters(SymbolicModel const& model) { + return model->getParameters(); +}*/ + template std::function const&)> getModelInfoPrinter(std::string name = "Model") { // look, C++ has lambdas and stuff! @@ -231,6 +240,7 @@ void define_sparse_model(py::module& m) { py::class_, std::shared_ptr>, ModelBase> modelRatFunc(m, "_SparseParametricModel", "A probabilistic model where transitions are represented by rational functions and saved in a sparse matrix"); modelRatFunc.def("collect_probability_parameters", &probabilityVariables, "Collect parameters") .def("collect_reward_parameters", &rewardVariables, "Collect reward parameters") + .def("collect_all_parameters", &allVariables, "Collect all parameters") .def_property_readonly("labeling", &getLabeling, "Labels") .def("labels_state", &SparseModel::getLabelsOfState, py::arg("state"), "Get labels of state") .def_property_readonly("initial_states", &getSparseInitialStates, "Initial states") @@ -313,8 +323,7 @@ void define_symbolic_model(py::module& m, std::string vt_suffix) { // Parametric models py::class_, std::shared_ptr>, ModelBase> modelRatFunc(m, ("_"+prefixParametricClassName+"Model").c_str(), "A probabilistic model where transitions are represented by rational functions and saved in a symbolic representation"); - modelRatFunc.def("collect_probability_parameters", &probabilityVariables, "Collect parameters") - .def("collect_reward_parameters", &rewardVariables, "Collect reward parameters") + modelRatFunc.def("get_parameters", &SymbolicModel::getParameters, "Get parameters") .def_property_readonly("reward_models", [](SymbolicModel const& model) {return model.getRewardModels(); }, "Reward models") .def("reduce_to_state_based_rewards", &SymbolicModel::reduceToStateBasedRewards) .def("__str__", getModelInfoPrinter("ParametricModel")) diff --git a/tests/pars/test_parametric.py b/tests/pars/test_parametric.py index 677a6ff..77f1fc2 100644 --- a/tests/pars/test_parametric.py +++ b/tests/pars/test_parametric.py @@ -50,6 +50,33 @@ class TestParametric: values = result.get_values() assert len(values) == 3 + def test_parameters(self): + program = stormpy.parse_prism_program(get_example_path("pdtmc", "brp16_2.pm")) + formulas = stormpy.parse_properties_for_prism_program("P=? [F s=5]", program) + model = stormpy.build_parametric_model(program, formulas) + model_parameters = model.collect_probability_parameters() + reward_parameters = model.collect_reward_parameters() + all_parameters = model.collect_all_parameters() + assert len(model_parameters) == 2 + assert len(reward_parameters) == 0 + assert len(all_parameters) == 2 + + program_reward = stormpy.parse_prism_program(get_example_path("pdtmc", "brp_rewards16_2.pm")) + formulas_reward = stormpy.parse_properties_for_prism_program("Rmin=? [ F \"target\" ]", program_reward) + model = stormpy.build_parametric_model(program_reward, formulas_reward) + model_parameters = model.collect_probability_parameters() + reward_parameters = model.collect_reward_parameters() + all_parameters = model.collect_all_parameters() + assert len(model_parameters) == 2 + assert len(reward_parameters) == 2 + assert len(all_parameters) == 4 + + model = stormpy.build_symbolic_parametric_model(program, formulas) + assert len(model.get_parameters()) == 4 + + model = stormpy.build_symbolic_parametric_model(program_reward, formulas_reward) + assert len(model.get_parameters()) == 4 + def test_constraints_collector(self): from pycarl.formula import FormulaType, Relation if stormpy.info.storm_ratfunc_use_cln(): From aae472389e72e1c83c865b83bde7891d5af4458c Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 4 Apr 2019 17:03:25 +0200 Subject: [PATCH 140/147] Added test for scheduler --- tests/storage/test_scheduler.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/storage/test_scheduler.py diff --git a/tests/storage/test_scheduler.py b/tests/storage/test_scheduler.py new file mode 100644 index 0000000..b1bbefe --- /dev/null +++ b/tests/storage/test_scheduler.py @@ -0,0 +1,30 @@ +import stormpy +import stormpy.logic +from helpers.helper import get_example_path +import pytest + + +class TestScheduler: + + def test_scheduler_mdp(self): + program = stormpy.parse_prism_program(get_example_path("mdp", "coin2-2.nm")) + formulas = stormpy.parse_properties_for_prism_program("Pmin=? [ F \"finished\" & \"all_coins_equal_1\"]", program) + model = stormpy.build_model(program, formulas) + assert model.nr_states == 272 + assert model.nr_transitions == 492 + assert len(model.initial_states) == 1 + initial_state = model.initial_states[0] + assert initial_state == 0 + result = stormpy.model_checking(model, formulas[0], extract_scheduler=True) + assert result.has_scheduler + scheduler = result.scheduler + assert scheduler.memoryless + assert scheduler.memory_size == 1 + assert scheduler.deterministic + for state in model.states: + choice = scheduler.get_choice(state) + assert choice.defined + assert choice.deterministic + action = choice.get_deterministic_choice() + assert 0 <= action + assert action < len(state.actions) From e148fbcd6afcdee9869564c7379e14ae244997fc Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 4 Apr 2019 17:03:56 +0200 Subject: [PATCH 141/147] Added documenation for schedulers --- doc/source/advanced_topics.rst | 1 + doc/source/doc/schedulers.rst | 60 ++++++++++++++++++++++++++++ examples/schedulers/01-schedulers.py | 18 +++++---- 3 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 doc/source/doc/schedulers.rst diff --git a/doc/source/advanced_topics.rst b/doc/source/advanced_topics.rst index bc32885..62142be 100644 --- a/doc/source/advanced_topics.rst +++ b/doc/source/advanced_topics.rst @@ -12,6 +12,7 @@ This guide is a collection of examples meant to bridge the gap between the getti doc/engines doc/exploration doc/reward_models + doc/schedulers doc/shortest_paths doc/parametric_models doc/dfts \ No newline at end of file diff --git a/doc/source/doc/schedulers.rst b/doc/source/doc/schedulers.rst new file mode 100644 index 0000000..b60bdd1 --- /dev/null +++ b/doc/source/doc/schedulers.rst @@ -0,0 +1,60 @@ +*********************** +Working with Schedulers +*********************** + +In non-deterministic models the notion of a scheduler (or policy) is important. +The scheduler determines which action to take at each state. + +For a given reachability property, Storm can return the scheduler realizing the resulting probability. + +Examining Schedulers +==================== + +.. seealso:: `01-schedulers.py `_ + +As in :doc:`../getting_started`, we import some required modules and build a model from the example files:: + + >>> import stormpy + >>> import stormpy.core + >>> import stormpy.examples + >>> import stormpy.examples.files + + >>> path = stormpy.examples.files.prism_mdp_coin_2_2 + >>> formula_str = "Pmin=? [F \"finished\" & \"all_coins_equal_1\"]" + >>> program = stormpy.parse_prism_program(path) + >>> formulas = stormpy.parse_properties_for_prism_program(formula_str, program) + >>> model = stormpy.build_model(program, formulas) + +Next we check the model and make sure to extract the scheduler: + + >>> result = stormpy.model_checking(model, formulas[0], extract_scheduler=True) + +The result then contains the scheduler we want: + >>> assert result.has_scheduler + >>> scheduler = result.scheduler + >>> assert scheduler.memoryless + >>> assert scheduler.deterministic + >>> print(scheduler) + ___________________________________________________________________ + Fully defined memoryless deterministic scheduler: + model state: choice(s) + 0 0 + 1 0 + 2 1 + 3 0 + -etc- + +To get the information which action the scheduler chooses in which state, we can simply iterate over the states: + + >>> for state in model.states: + ... choice = scheduler.get_choice(state) + ... action = choice.get_deterministic_choice() + ... print("In state {} choose action {}".format(state, action)) + In state 0 choose action 0 + In state 1 choose action 0 + In state 2 choose action 1 + In state 3 choose action 0 + In state 4 choose action 0 + In state 5 choose action 0 + -etc- + diff --git a/examples/schedulers/01-schedulers.py b/examples/schedulers/01-schedulers.py index 6a0b2bd..14a8069 100644 --- a/examples/schedulers/01-schedulers.py +++ b/examples/schedulers/01-schedulers.py @@ -15,15 +15,17 @@ def example_schedulers_01(): model = stormpy.build_model(program, formulas) initial_state = model.initial_states[0] assert initial_state == 0 - result = stormpy.model_checking(model, formulas[0], extract_scheduler = True) + result = stormpy.model_checking(model, formulas[0], extract_scheduler=True) assert result.has_scheduler - print(result.scheduler) - assert result.scheduler.memoryless - assert result.scheduler.deterministic - - for i in range(0,model.nr_states): - print("In state {} choose action {}".format(i,result.scheduler.get_choice(i).get_deterministic_choice())) - + scheduler = result.scheduler + print(scheduler) + assert scheduler.memoryless + assert scheduler.deterministic + + for state in model.states: + choice = scheduler.get_choice(state) + action = choice.get_deterministic_choice() + print("In state {} choose action {}".format(state, action)) if __name__ == '__main__': From 37d82d4e0ce2457e10f876bdbeea58183e559ede Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Thu, 4 Apr 2019 17:04:07 +0200 Subject: [PATCH 142/147] Fixed typo in doc --- doc/source/doc/shortest_paths.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/doc/shortest_paths.rst b/doc/source/doc/shortest_paths.rst index a727950..6001a5d 100644 --- a/doc/source/doc/shortest_paths.rst +++ b/doc/source/doc/shortest_paths.rst @@ -18,7 +18,7 @@ It is crucial to note that *any* path is eligible, including those that (repeate Examining Shortest Paths ======================== -.. seealso:: `07-shortest-paths.py `_ +.. seealso:: `01-shortest-paths.py `_ As in :doc:`../getting_started`, we import some required modules and build a model from the example files:: From 341bd544e3d1afd9281ac19de2b2274769b6f52d Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 5 Apr 2019 11:47:10 +0200 Subject: [PATCH 143/147] Added tests for MAs: scheduler extraction and transformation to MDPs --- tests/core/test_transformation.py | 28 ++++++++++++++++++++--- tests/storage/test_scheduler.py | 37 ++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/tests/core/test_transformation.py b/tests/core/test_transformation.py index 803c57e..b1ffe99 100644 --- a/tests/core/test_transformation.py +++ b/tests/core/test_transformation.py @@ -6,6 +6,7 @@ import math class TestTransformation: + def test_transform_symbolic_dtmc_to_sparse(self): program = stormpy.parse_prism_program(get_example_path("dtmc", "crowds5_5.pm")) symbolic_model = stormpy.build_symbolic_model(program) @@ -36,10 +37,9 @@ class TestTransformation: assert symbolic_model.supports_parameters assert type(symbolic_model) is stormpy.SparseParametricDtmc - def test_transform_continuous_to_discrete_time_model(self): + def test_transform_continuous_to_discrete_time_model_ctmc(self): ctmc = stormpy.build_model_from_drn(get_example_path("ctmc", "dft.drn")) formulas = stormpy.parse_properties("T=? [ F \"failed\" ]") - print(formulas) assert ctmc.nr_states == 16 assert ctmc.nr_transitions == 33 assert len(ctmc.initial_states) == 1 @@ -49,7 +49,6 @@ class TestTransformation: assert math.isclose(result.at(initial_state), 4.166666667) dtmc, dtmc_formulas = stormpy.transform_to_discrete_time_model(ctmc, formulas) - print(dtmc_formulas) assert dtmc.nr_states == 16 assert dtmc.nr_transitions == 33 assert len(dtmc.initial_states) == 1 @@ -57,3 +56,26 @@ class TestTransformation: assert initial_state == 1 result = stormpy.model_checking(dtmc, dtmc_formulas[0]) assert math.isclose(result.at(initial_state), 4.166666667) + + def test_transform_continuous_to_discrete_time_model_ma(self): + program = stormpy.parse_prism_program(get_example_path("ma", "simple.ma"), False, True) + formulas = stormpy.parse_properties_for_prism_program("Tmin=? [ F s=4 ]", program) + ma = stormpy.build_model(program, formulas) + assert ma.nr_states == 5 + assert ma.nr_transitions == 8 + assert ma.model_type == stormpy.ModelType.MA + assert len(ma.initial_states) == 1 + initial_state = ma.initial_states[0] + assert initial_state == 0 + result = stormpy.model_checking(ma, formulas[0]) + assert math.isclose(result.at(initial_state), 0.08333333333) + + mdp, mdp_formulas = stormpy.transform_to_discrete_time_model(ma, formulas) + assert mdp.nr_states == 5 + assert mdp.nr_transitions == 8 + assert mdp.model_type == stormpy.ModelType.MDP + assert len(mdp.initial_states) == 1 + initial_state = mdp.initial_states[0] + assert initial_state == 0 + result = stormpy.model_checking(mdp, mdp_formulas[0]) + assert math.isclose(result.at(initial_state), 0.08333333333) diff --git a/tests/storage/test_scheduler.py b/tests/storage/test_scheduler.py index b1bbefe..c1ec7c8 100644 --- a/tests/storage/test_scheduler.py +++ b/tests/storage/test_scheduler.py @@ -1,7 +1,8 @@ import stormpy import stormpy.logic from helpers.helper import get_example_path -import pytest + +import math class TestScheduler: @@ -28,3 +29,37 @@ class TestScheduler: action = choice.get_deterministic_choice() assert 0 <= action assert action < len(state.actions) + + def test_scheduler_ma_via_mdp(self): + program = stormpy.parse_prism_program(get_example_path("ma", "simple.ma"), False, True) + formulas = stormpy.parse_properties_for_prism_program("Tmin=? [ F s=4 ]", program) + ma = stormpy.build_model(program, formulas) + assert ma.nr_states == 5 + assert ma.nr_transitions == 8 + assert ma.model_type == stormpy.ModelType.MA + + # Convert MA to MDP + mdp, mdp_formulas = stormpy.transform_to_discrete_time_model(ma, formulas) + assert mdp.nr_states == 5 + assert mdp.nr_transitions == 8 + assert mdp.model_type == stormpy.ModelType.MDP + assert len(mdp.initial_states) == 1 + initial_state = mdp.initial_states[0] + assert initial_state == 0 + + result = stormpy.model_checking(mdp, mdp_formulas[0], extract_scheduler=True) + assert math.isclose(result.at(initial_state), 0.08333333333) + assert result.has_scheduler + scheduler = result.scheduler + assert scheduler.memoryless + assert scheduler.memory_size == 1 + assert scheduler.deterministic + for state in mdp.states: + choice = scheduler.get_choice(state) + assert choice.defined + assert choice.deterministic + action = choice.get_deterministic_choice() + if state.id == 0: + assert action == 1 + else: + assert action == 0 From 72bbb161b3fdce22a48b6289461c9d432b473c69 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 5 Apr 2019 11:50:01 +0200 Subject: [PATCH 144/147] Removed print statement in tests --- tests/storage/test_expressions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/storage/test_expressions.py b/tests/storage/test_expressions.py index 0e82122..8ba8e4c 100644 --- a/tests/storage/test_expressions.py +++ b/tests/storage/test_expressions.py @@ -36,6 +36,8 @@ class TestExpressions: manager = stormpy.ExpressionManager() ep = stormpy.ExpressionParser(manager) ep.set_identifier_mapping(dict()) - print(ep.parse("true")) - print(ep.parse("1.0")) - assert ep.parse("1.0").has_rational_type() + ex_true = ep.parse("true") + assert str(ex_true) == "true" + ex_one = ep.parse("1.0") + assert str(ex_one) == "1" + assert ex_one.has_rational_type() From 0595ee1d64d3ebef4444c8a2cb9590eea253b23b Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 5 Apr 2019 11:51:08 +0200 Subject: [PATCH 145/147] Added documentation for MA scheduler extraction --- doc/source/doc/schedulers.rst | 43 ++++++++++++++++++++++++++-- examples/schedulers/01-schedulers.py | 1 - examples/schedulers/02-schedulers.py | 37 ++++++++++++++++++++++++ lib/stormpy/examples/files.py | 2 ++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 examples/schedulers/02-schedulers.py diff --git a/doc/source/doc/schedulers.rst b/doc/source/doc/schedulers.rst index b60bdd1..00b03bb 100644 --- a/doc/source/doc/schedulers.rst +++ b/doc/source/doc/schedulers.rst @@ -7,8 +7,8 @@ The scheduler determines which action to take at each state. For a given reachability property, Storm can return the scheduler realizing the resulting probability. -Examining Schedulers -==================== +Examining Schedulers for MDPs +============================= .. seealso:: `01-schedulers.py `_ @@ -30,6 +30,7 @@ Next we check the model and make sure to extract the scheduler: >>> result = stormpy.model_checking(model, formulas[0], extract_scheduler=True) The result then contains the scheduler we want: + >>> assert result.has_scheduler >>> scheduler = result.scheduler >>> assert scheduler.memoryless @@ -58,3 +59,41 @@ To get the information which action the scheduler chooses in which state, we can In state 5 choose action 0 -etc- + +Examining Schedulers for Markov automata +======================================== + +.. seealso:: `02-schedulers.py `_ + +Currently there is no support yet for scheduler extraction on MAs. +However, if the timing information is not relevant for the property, we can circumvent this lack by first transforming the MA to an MDP. + +We build the model as before: + + >>> path = stormpy.examples.files.prism_ma_simple + >>> formula_str = "Tmin=? [ F s=4 ]" + + >>> program = stormpy.parse_prism_program(path, False, True) + >>> formulas = stormpy.parse_properties_for_prism_program(formula_str, program) + >>> ma = stormpy.build_model(program, formulas) + +Next we transform the continuous-time model into a discrete-time model. +Note that all timing information is lost at this point. + + >>> mdp, mdp_formulas = stormpy.transform_to_discrete_time_model(ma, formulas) + >>> assert mdp.model_type == stormpy.ModelType.MDP + +After the transformation we have obtained an MDP where we can extract the scheduler as shown before: + + >>> result = stormpy.model_checking(mdp, mdp_formulas[0], extract_scheduler=True) + >>> scheduler = result.scheduler + >>> print(scheduler) + ___________________________________________________________________ + Fully defined memoryless deterministic scheduler: + model state: choice(s) + 0 1 + 1 0 + 2 0 + 3 0 + 4 0 + -etc- diff --git a/examples/schedulers/01-schedulers.py b/examples/schedulers/01-schedulers.py index 14a8069..3cc40bc 100644 --- a/examples/schedulers/01-schedulers.py +++ b/examples/schedulers/01-schedulers.py @@ -7,7 +7,6 @@ import stormpy.examples.files def example_schedulers_01(): path = stormpy.examples.files.prism_mdp_coin_2_2 - formula_str = "Pmin=? [F \"finished\" & \"all_coins_equal_1\"]" program = stormpy.parse_prism_program(path) diff --git a/examples/schedulers/02-schedulers.py b/examples/schedulers/02-schedulers.py new file mode 100644 index 0000000..9ab79a7 --- /dev/null +++ b/examples/schedulers/02-schedulers.py @@ -0,0 +1,37 @@ +import stormpy +import stormpy.core + +import stormpy.examples +import stormpy.examples.files + + +def example_schedulers_02(): + path = stormpy.examples.files.prism_ma_simple + formula_str = "Tmin=? [ F s=4 ]" + + program = stormpy.parse_prism_program(path, False, True) + formulas = stormpy.parse_properties_for_prism_program(formula_str, program) + ma = stormpy.build_model(program, formulas) + assert ma.model_type == stormpy.ModelType.MA + + # Convert MA to MDP + mdp, mdp_formulas = stormpy.transform_to_discrete_time_model(ma, formulas) + assert mdp.model_type == stormpy.ModelType.MDP + initial_state = mdp.initial_states[0] + assert initial_state == 0 + + result = stormpy.model_checking(mdp, mdp_formulas[0], extract_scheduler=True) + assert result.has_scheduler + scheduler = result.scheduler + print(scheduler) + assert scheduler.memoryless + assert scheduler.deterministic + + for state in mdp.states: + choice = scheduler.get_choice(state) + action = choice.get_deterministic_choice() + print("In state {} choose action {}".format(state, action)) + + +if __name__ == '__main__': + example_schedulers_02() diff --git a/lib/stormpy/examples/files.py b/lib/stormpy/examples/files.py index 5e7a447..48a51e5 100644 --- a/lib/stormpy/examples/files.py +++ b/lib/stormpy/examples/files.py @@ -19,6 +19,8 @@ prism_pdtmc_die = _path("pdtmc", "parametric_die.pm") """Knuth Yao Die -- 2 unfair coins Example""" prism_dtmc_brp = _path("dtmc", "brp-16-2.pm") """Bounded Retransmission Protocol""" +prism_ma_simple = _path("ma", "simple.ma") +"""Prism file for a simple Markov automaton""" drn_ctmc_dft = _path("ctmc", "dft.drn") """DRN format for a CTMC from a DFT""" drn_pdtmc_die = _path("pdtmc", "die.drn") From 65cd8f4787488c0a6bf0fd2ae7abad4fb6f4f8d2 Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Fri, 12 Apr 2019 11:05:25 +0200 Subject: [PATCH 146/147] Adaption to changes in Storm-dft --- src/dft/analysis.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dft/analysis.cpp b/src/dft/analysis.cpp index 3edbdf4..cc1c285 100644 --- a/src/dft/analysis.cpp +++ b/src/dft/analysis.cpp @@ -7,8 +7,10 @@ // Thin wrapper for DFT analysis template -std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, bool enableDC) { - typename storm::modelchecker::DFTModelChecker::dft_results dftResults = storm::api::analyzeDFT(dft, properties, symred, allowModularisation, enableDC, false); +//std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents = {}, double approximationError = 0.0, storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH) { +std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents) { + typename storm::modelchecker::DFTModelChecker::dft_results dftResults = storm::api::analyzeDFT(dft, properties, symred, allowModularisation, relevantEvents, 0.0, storm::builder::ApproximationHeuristic::DEPTH, false); + std::vector results; for (auto result : dftResults) { results.push_back(boost::get(result)); @@ -20,6 +22,6 @@ std::vector analyzeDFT(storm::storage::DFT const& dft, std // Define python bindings void define_analysis(py::module& m) { - m.def("analyze_dft", &analyzeDFT, "Analyze the DFT", py::arg("dft"), py::arg("properties"), py::arg("symred")=true, py::arg("allow_modularisation")=false, py::arg("enable_dont_care")=true); + m.def("analyze_dft", &analyzeDFT, "Analyze the DFT", py::arg("dft"), py::arg("properties"), py::arg("symred")=true, py::arg("allow_modularisation")=false, py::arg("relevant_events")=std::set()); } From 2bd53218ad2bd71691182bdf6dab2d4912722dfc Mon Sep 17 00:00:00 2001 From: Matthias Volk Date: Wed, 17 Apr 2019 23:43:29 +0200 Subject: [PATCH 147/147] Adaptions to changes in Storm-dft --- src/dft/analysis.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dft/analysis.cpp b/src/dft/analysis.cpp index cc1c285..fd15ea7 100644 --- a/src/dft/analysis.cpp +++ b/src/dft/analysis.cpp @@ -8,8 +8,8 @@ // Thin wrapper for DFT analysis template //std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents = {}, double approximationError = 0.0, storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH) { -std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents) { - typename storm::modelchecker::DFTModelChecker::dft_results dftResults = storm::api::analyzeDFT(dft, properties, symred, allowModularisation, relevantEvents, 0.0, storm::builder::ApproximationHeuristic::DEPTH, false); +std::vector analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents, bool allowDCForRelevant) { + typename storm::modelchecker::DFTModelChecker::dft_results dftResults = storm::api::analyzeDFT(dft, properties, symred, allowModularisation, relevantEvents, allowDCForRelevant, 0.0, storm::builder::ApproximationHeuristic::DEPTH, false); std::vector results; for (auto result : dftResults) { @@ -22,6 +22,6 @@ std::vector analyzeDFT(storm::storage::DFT const& dft, std // Define python bindings void define_analysis(py::module& m) { - m.def("analyze_dft", &analyzeDFT, "Analyze the DFT", py::arg("dft"), py::arg("properties"), py::arg("symred")=true, py::arg("allow_modularisation")=false, py::arg("relevant_events")=std::set()); + m.def("analyze_dft", &analyzeDFT, "Analyze the DFT", py::arg("dft"), py::arg("properties"), py::arg("symred")=true, py::arg("allow_modularisation")=false, py::arg("relevant_events")=std::set(), py::arg("dc_for_relevant")=true); }