You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

494 lines
21 KiB

4 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. import sys
  2. if sys.version_info[0] == 2:
  3. raise ImportError('Python 2.x is not supported for stormpy.')
  4. from . import core
  5. from .core import *
  6. from . import storage
  7. from .storage import *
  8. from ._config import *
  9. from .logic import *
  10. from .exceptions import *
  11. from pycarl import Variable # needed for building parametric models
  12. __version__ = "unknown"
  13. try:
  14. from ._version import __version__
  15. except ImportError:
  16. # We're running in a tree that doesn't have a _version.py, so we don't know what our version is.
  17. pass
  18. core._set_up("")
  19. def _convert_sparse_model(model, parametric=False):
  20. """
  21. Convert (parametric) model in sparse representation into model corresponding to exact model type.
  22. :param model: Sparse model.
  23. :param parametric: Flag indicating if the model is parametric.
  24. :return: Model corresponding to exact model type.
  25. """
  26. if parametric:
  27. assert model.supports_parameters
  28. if model.model_type == ModelType.DTMC:
  29. return model._as_sparse_pdtmc()
  30. elif model.model_type == ModelType.MDP:
  31. return model._as_sparse_pmdp()
  32. elif model.model_type == ModelType.POMDP:
  33. return model._as_sparse_ppomdp()
  34. elif model.model_type == ModelType.CTMC:
  35. return model._as_sparse_pctmc()
  36. elif model.model_type == ModelType.MA:
  37. return model._as_sparse_pma()
  38. else:
  39. raise StormError("Not supported parametric model constructed")
  40. else:
  41. assert not model.supports_parameters
  42. if model.model_type == ModelType.DTMC:
  43. return model._as_sparse_dtmc()
  44. elif model.model_type == ModelType.MDP:
  45. return model._as_sparse_mdp()
  46. elif model.model_type == ModelType.POMDP:
  47. return model._as_sparse_pomdp()
  48. elif model.model_type == ModelType.CTMC:
  49. return model._as_sparse_ctmc()
  50. elif model.model_type == ModelType.MA:
  51. return model._as_sparse_ma()
  52. else:
  53. raise StormError("Not supported non-parametric model constructed")
  54. def _convert_symbolic_model(model, parametric=False):
  55. """
  56. Convert (parametric) model in symbolic representation into model corresponding to exact model type.
  57. :param model: Symbolic model.
  58. :param parametric: Flag indicating if the model is parametric.
  59. :return: Model corresponding to exact model type.
  60. """
  61. if parametric:
  62. assert model.supports_parameters
  63. if model.model_type == ModelType.DTMC:
  64. return model._as_symbolic_pdtmc()
  65. elif model.model_type == ModelType.MDP:
  66. return model._as_symbolic_pmdp()
  67. elif model.model_type == ModelType.CTMC:
  68. return model._as_symbolic_pctmc()
  69. elif model.model_type == ModelType.MA:
  70. return model._as_symbolic_pma()
  71. else:
  72. raise StormError("Not supported parametric model constructed")
  73. else:
  74. assert not model.supports_parameters
  75. if model.model_type == ModelType.DTMC:
  76. return model._as_symbolic_dtmc()
  77. elif model.model_type == ModelType.MDP:
  78. return model._as_symbolic_mdp()
  79. elif model.model_type == ModelType.CTMC:
  80. return model._as_symbolic_ctmc()
  81. elif model.model_type == ModelType.MA:
  82. return model._as_symbolic_ma()
  83. else:
  84. raise StormError("Not supported non-parametric model constructed")
  85. def build_model(symbolic_description, properties=None):
  86. """
  87. Build a model in sparse representation from a symbolic description.
  88. :param symbolic_description: Symbolic model description to translate into a model.
  89. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved.
  90. :return: Model in sparse representation.
  91. """
  92. return build_sparse_model(symbolic_description, properties=properties)
  93. def build_parametric_model(symbolic_description, properties=None):
  94. """
  95. Build a parametric model in sparse representation from a symbolic description.
  96. :param symbolic_description: Symbolic model description to translate into a model.
  97. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved.
  98. :return: Parametric model in sparse representation.
  99. """
  100. return build_sparse_parametric_model(symbolic_description, properties=properties)
  101. def build_sparse_model(symbolic_description, properties=None):
  102. """
  103. Build a model in sparse representation from a symbolic description.
  104. :param symbolic_description: Symbolic model description to translate into a model.
  105. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved.
  106. :return: Model in sparse representation.
  107. """
  108. if not symbolic_description.undefined_constants_are_graph_preserving:
  109. raise StormError("Program still contains undefined constants")
  110. if properties:
  111. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  112. intermediate = core._build_sparse_model_from_symbolic_description(symbolic_description, formulae)
  113. else:
  114. intermediate = core._build_sparse_model_from_symbolic_description(symbolic_description)
  115. return _convert_sparse_model(intermediate, parametric=False)
  116. def build_sparse_parametric_model(symbolic_description, properties=None):
  117. """
  118. Build a parametric model in sparse representation from a symbolic description.
  119. :param symbolic_description: Symbolic model description to translate into a model.
  120. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved.
  121. :return: Parametric model in sparse representation.
  122. """
  123. if not symbolic_description.undefined_constants_are_graph_preserving:
  124. raise StormError("Program still contains undefined constants")
  125. if properties:
  126. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  127. intermediate = core._build_sparse_parametric_model_from_symbolic_description(symbolic_description, formulae)
  128. else:
  129. intermediate = core._build_sparse_parametric_model_from_symbolic_description(symbolic_description)
  130. return _convert_sparse_model(intermediate, parametric=True)
  131. def build_symbolic_model(symbolic_description, properties=None):
  132. """
  133. Build a model in symbolic representation from a symbolic description.
  134. :param symbolic_description: Symbolic model description to translate into a model.
  135. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved.
  136. :return: Model in symbolic representation.
  137. """
  138. if not symbolic_description.undefined_constants_are_graph_preserving:
  139. raise StormError("Program still contains undefined constants")
  140. if properties:
  141. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  142. intermediate = core._build_symbolic_model_from_symbolic_description(symbolic_description, formulae)
  143. else:
  144. intermediate = core._build_symbolic_model_from_symbolic_description(symbolic_description)
  145. return _convert_symbolic_model(intermediate, parametric=False)
  146. def build_symbolic_parametric_model(symbolic_description, properties=None):
  147. """
  148. Build a parametric model in symbolic representation from a symbolic description.
  149. :param symbolic_description: Symbolic model description to translate into a model.
  150. :param List[Property] properties: List of properties that should be preserved during the translation. If None, then all properties are preserved.
  151. :return: Parametric model in symbolic representation.
  152. """
  153. if not symbolic_description.undefined_constants_are_graph_preserving:
  154. raise StormError("Program still contains undefined constants")
  155. if properties:
  156. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  157. intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description, formulae)
  158. else:
  159. intermediate = core._build_symbolic_parametric_model_from_symbolic_description(symbolic_description)
  160. return _convert_symbolic_model(intermediate, parametric=True)
  161. def build_model_from_drn(file, options = DirectEncodingParserOptions()):
  162. """
  163. Build a model in sparse representation from the explicit DRN representation.
  164. :param String file: DRN file containing the model.
  165. :param DirectEncodingParserOptions: Options for the parser.
  166. :return: Model in sparse representation.
  167. """
  168. intermediate = core._build_sparse_model_from_drn(file, options)
  169. return _convert_sparse_model(intermediate, parametric=False)
  170. def build_parametric_model_from_drn(file, options = DirectEncodingParserOptions()):
  171. """
  172. Build a parametric model in sparse representation from the explicit DRN representation.
  173. :param String file: DRN file containing the model.
  174. :param DirectEncodingParserOptions: Options for the parser.
  175. :return: Parametric model in sparse representation.
  176. """
  177. intermediate = core._build_sparse_parametric_model_from_drn(file, options)
  178. return _convert_sparse_model(intermediate, parametric=True)
  179. def perform_bisimulation(model, properties, bisimulation_type):
  180. """
  181. Perform bisimulation on model.
  182. :param model: Model.
  183. :param properties: Properties to preserve during bisimulation.
  184. :param bisimulation_type: Type of bisimulation (weak or strong).
  185. :return: Model after bisimulation.
  186. """
  187. return perform_sparse_bisimulation(model, properties, bisimulation_type)
  188. def perform_sparse_bisimulation(model, properties, bisimulation_type):
  189. """
  190. Perform bisimulation on model in sparse representation.
  191. :param model: Model.
  192. :param properties: Properties to preserve during bisimulation.
  193. :param bisimulation_type: Type of bisimulation (weak or strong).
  194. :return: Model after bisimulation.
  195. """
  196. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  197. if model.supports_parameters:
  198. return core._perform_parametric_bisimulation(model, formulae, bisimulation_type)
  199. else:
  200. return core._perform_bisimulation(model, formulae, bisimulation_type)
  201. def perform_symbolic_bisimulation(model, properties):
  202. """
  203. Perform bisimulation on model in symbolic representation.
  204. :param model: Model.
  205. :param properties: Properties to preserve during bisimulation.
  206. :return: Model after bisimulation.
  207. """
  208. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  209. bisimulation_type = BisimulationType.STRONG
  210. if model.supports_parameters:
  211. return core._perform_symbolic_parametric_bisimulation(model, formulae, bisimulation_type)
  212. else:
  213. return core._perform_symbolic_bisimulation(model, formulae, bisimulation_type)
  214. def model_checking(model, property, only_initial_states=False, extract_scheduler=False, environment=Environment()):
  215. """
  216. Perform model checking on model for property.
  217. :param model: Model.
  218. :param property: Property to check for.
  219. :param only_initial_states: If True, only results for initial states are computed, otherwise for all states.
  220. :param extract_scheduler: If True, try to extract a scheduler
  221. :return: Model checking result.
  222. :rtype: CheckResult
  223. """
  224. if model.is_sparse_model:
  225. return check_model_sparse(model, property, only_initial_states=only_initial_states,
  226. extract_scheduler=extract_scheduler, environment=environment)
  227. else:
  228. assert (model.is_symbolic_model)
  229. if extract_scheduler:
  230. raise StormError("Model checking based on dd engine does not support extracting schedulers right now.")
  231. return check_model_dd(model, property, only_initial_states=only_initial_states,
  232. environment=environment)
  233. def check_model_sparse(model, property, only_initial_states=False, extract_scheduler=False, environment=Environment()):
  234. """
  235. Perform model checking on model for property.
  236. :param model: Model.
  237. :param property: Property to check for.
  238. :param only_initial_states: If True, only results for initial states are computed, otherwise for all states.
  239. :param extract_scheduler: If True, try to extract a scheduler
  240. :return: Model checking result.
  241. :rtype: CheckResult
  242. """
  243. if isinstance(property, Property):
  244. formula = property.raw_formula
  245. else:
  246. formula = property
  247. if model.supports_parameters:
  248. task = core.ParametricCheckTask(formula, only_initial_states)
  249. task.set_produce_schedulers(extract_scheduler)
  250. return core._parametric_model_checking_sparse_engine(model, task, environment=environment)
  251. else:
  252. task = core.CheckTask(formula, only_initial_states)
  253. task.set_produce_schedulers(extract_scheduler)
  254. return core._model_checking_sparse_engine(model, task, environment=environment)
  255. def check_model_dd(model, property, only_initial_states=False, environment=Environment()):
  256. """
  257. Perform model checking using dd engine.
  258. :param model: Model.
  259. :param property: Property to check for.
  260. :param only_initial_states: If True, only results for initial states are computed, otherwise for all states.
  261. :return: Model checking result.
  262. :rtype: CheckResult
  263. """
  264. if isinstance(property, Property):
  265. formula = property.raw_formula
  266. else:
  267. formula = property
  268. if model.supports_parameters:
  269. task = core.ParametricCheckTask(formula, only_initial_states)
  270. return core._parametric_model_checking_dd_engine(model, task, environment=environment)
  271. else:
  272. task = core.CheckTask(formula, only_initial_states)
  273. return core._model_checking_dd_engine(model, task, environment=environment)
  274. def check_model_hybrid(model, property, only_initial_states=False, environment=Environment()):
  275. """
  276. Perform model checking using hybrid engine.
  277. :param model: Model.
  278. :param property: Property to check for.
  279. :param only_initial_states: If True, only results for initial states are computed, otherwise for all states.
  280. :return: Model checking result.
  281. :rtype: CheckResult
  282. """
  283. if isinstance(property, Property):
  284. formula = property.raw_formula
  285. else:
  286. formula = property
  287. if model.supports_parameters:
  288. task = core.ParametricCheckTask(formula, only_initial_states)
  289. return core._parametric_model_checking_hybrid_engine(model, task, environment=environment)
  290. else:
  291. task = core.CheckTask(formula, only_initial_states)
  292. return core._model_checking_hybrid_engine(model, task, environment=environment)
  293. def transform_to_sparse_model(model):
  294. """
  295. Transform model in symbolic representation into model in sparse representation.
  296. :param model: Symbolic model.
  297. :return: Sparse model.
  298. """
  299. if model.supports_parameters:
  300. return core._transform_to_sparse_parametric_model(model)
  301. else:
  302. return core._transform_to_sparse_model(model)
  303. def transform_to_discrete_time_model(model, properties):
  304. """
  305. Transform continuous-time model to discrete time model.
  306. :param model: Continuous-time model.
  307. :param properties: List of properties to transform as well.
  308. :return: Tuple (Discrete-time model, converted properties).
  309. """
  310. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  311. if model.supports_parameters:
  312. return core._transform_to_discrete_time_parametric_model(model, formulae)
  313. else:
  314. return core._transform_to_discrete_time_model(model, formulae)
  315. def eliminate_non_markovian_chains(ma, properties, label_behavior):
  316. """
  317. Eliminate chains of non-Markovian states if possible.
  318. :param ma: Markov automaton.
  319. :param properties: List of properties to transform as well.
  320. :param label_behavior: Behavior of labels while elimination.
  321. :return: Tuple (converted MA, converted properties).
  322. """
  323. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  324. if ma.supports_parameters:
  325. return core._eliminate_non_markovian_chains_parametric(ma, formulae, label_behavior)
  326. else:
  327. return core._eliminate_non_markovian_chains(ma, formulae, label_behavior)
  328. def prob01min_states(model, eventually_formula):
  329. assert type(eventually_formula) == logic.EventuallyFormula
  330. labelform = eventually_formula.subformula
  331. labelprop = core.Property("label-prop", labelform)
  332. phiStates = BitVector(model.nr_states, True)
  333. psiStates = model_checking(model, labelprop).get_truth_values()
  334. return compute_prob01min_states(model, phiStates, psiStates)
  335. def prob01max_states(model, eventually_formula):
  336. assert type(eventually_formula) == logic.EventuallyFormula
  337. labelform = eventually_formula.subformula
  338. labelprop = core.Property("label-prop", labelform)
  339. phiStates = BitVector(model.nr_states, True)
  340. psiStates = model_checking(model, labelprop).get_truth_values()
  341. return compute_prob01min_states(model, phiStates, psiStates)
  342. def compute_prob01_states(model, phi_states, psi_states):
  343. """
  344. Compute prob01 states for properties of the form phi_states until psi_states
  345. :param SparseDTMC model:
  346. :param BitVector phi_states:
  347. :param BitVector psi_states: Target states
  348. """
  349. if model.model_type != ModelType.DTMC:
  350. raise StormError("Prob 01 is only defined for DTMCs -- model must be a DTMC")
  351. if model.supports_parameters:
  352. return core._compute_prob01states_rationalfunc(model, phi_states, psi_states)
  353. else:
  354. return core._compute_prob01states_double(model, phi_states, psi_states)
  355. def compute_prob01min_states(model, phi_states, psi_states):
  356. if model.model_type == ModelType.DTMC:
  357. return compute_prob01_states(model, phi_states, psi_states)
  358. if model.supports_parameters:
  359. return core._compute_prob01states_min_rationalfunc(model, phi_states, psi_states)
  360. else:
  361. return core._compute_prob01states_min_double(model, phi_states, psi_states)
  362. def compute_prob01max_states(model, phi_states, psi_states):
  363. if model.model_type == ModelType.DTMC:
  364. return compute_prob01_states(model, phi_states, psi_states)
  365. if model.supports_parameters:
  366. return core._compute_prob01states_max_rationalfunc(model, phi_states, psi_states)
  367. else:
  368. return core._compute_prob01states_max_double(model, phi_states, psi_states)
  369. def topological_sort(model, forward=True, initial=[]):
  370. """
  371. :param model:
  372. :param forward:
  373. :return:
  374. """
  375. matrix = model.transition_matrix if forward else model.backward_transition_matrix
  376. if isinstance(model, storage._SparseParametricModel):
  377. return storage._topological_sort_rf(matrix, initial)
  378. elif isinstance(model, storage._SparseModel):
  379. return storage._topological_sort_double(matrix, initial)
  380. else:
  381. raise StormError("Unknown kind of model.")
  382. def construct_submodel(model, states, actions, keep_unreachable_states=True, options=SubsystemBuilderOptions()):
  383. """
  384. :param model: The model
  385. :param states: Which states should be preserved
  386. :param actions: Which actions should be preserved
  387. :param keep_unreachable_states: If False, run a reachability analysis.
  388. :return: A model with fewer states/actions
  389. """
  390. if isinstance(model, storage._SparseModel):
  391. return core._construct_subsystem_double(model, states, actions, keep_unreachable_states, options)
  392. else:
  393. raise NotImplementedError()
  394. def parse_properties(properties, context=None, filters=None):
  395. """
  396. :param properties: A string with the pctl properties
  397. :param context: A symbolic model that gives meaning to variables and constants.
  398. :param filters: filters, if applicable.
  399. :return: A list of properties
  400. """
  401. if context is None:
  402. return core.parse_properties_without_context(properties, filters)
  403. elif type(context) == core.SymbolicModelDescription:
  404. if context.is_prism_program():
  405. return core.parse_properties_for_prism_program(properties, context.as_prism_program(), filters)
  406. else:
  407. core.parse_properties_for_jani_program(properties, context.as_jani_model(), filters)
  408. elif type(context) == storage.PrismProgram:
  409. return core.parse_properties_for_prism_program(properties, context, filters)
  410. elif type(context) == storage.JaniModel:
  411. core.parse_properties_for_jani_model(properties, context, filters)
  412. else:
  413. raise StormError("Unclear context. Please pass a symbolic model description")