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.

530 lines
22 KiB

5 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, force_fully_observable=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, force_fully_observable=force_fully_observable, 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, force_fully_observable=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. :param force_fully_observable: If True, treat a POMDP as an MDP
  241. :return: Model checking result.
  242. :rtype: CheckResult
  243. """
  244. if isinstance(property, Property):
  245. formula = property.raw_formula
  246. else:
  247. formula = property
  248. if force_fully_observable:
  249. if model.is_partially_observable:
  250. # Note that casting a model to a fully observable model wont work with python/pybind, so we actually have other access points
  251. if model.supports_parameters:
  252. raise NotImplementedError("")
  253. elif model.is_exact:
  254. task = core.ExactCheckTask(formula, only_initial_states)
  255. task.set_produce_schedulers(extract_scheduler)
  256. return core._exact_model_checking_fully_observable(model, task, environment=environment)
  257. else:
  258. task = core.CheckTask(formula, only_initial_states)
  259. task.set_produce_schedulers(extract_scheduler)
  260. return core._model_checking_fully_observable(model, task, environment=environment)
  261. else:
  262. raise RuntimeError("Forcing models that are fully observable is not possible")
  263. if model.supports_parameters:
  264. task = core.ParametricCheckTask(formula, only_initial_states)
  265. task.set_produce_schedulers(extract_scheduler)
  266. return core._parametric_model_checking_sparse_engine(model, task, environment=environment)
  267. else:
  268. if model.is_exact:
  269. task = core.ExactCheckTask(formula, only_initial_states)
  270. task.set_produce_schedulers(extract_scheduler)
  271. return core._exact_model_checking_sparse_engine(model, task, environment=environment)
  272. else:
  273. task = core.CheckTask(formula, only_initial_states)
  274. task.set_produce_schedulers(extract_scheduler)
  275. return core._model_checking_sparse_engine(model, task, environment=environment)
  276. def check_model_dd(model, property, only_initial_states=False, environment=Environment()):
  277. """
  278. Perform model checking using dd engine.
  279. :param model: Model.
  280. :param property: Property to check for.
  281. :param only_initial_states: If True, only results for initial states are computed, otherwise for all states.
  282. :return: Model checking result.
  283. :rtype: CheckResult
  284. """
  285. if isinstance(property, Property):
  286. formula = property.raw_formula
  287. else:
  288. formula = property
  289. if model.supports_parameters:
  290. task = core.ParametricCheckTask(formula, only_initial_states)
  291. return core._parametric_model_checking_dd_engine(model, task, environment=environment)
  292. else:
  293. task = core.CheckTask(formula, only_initial_states)
  294. return core._model_checking_dd_engine(model, task, environment=environment)
  295. def check_model_hybrid(model, property, only_initial_states=False, environment=Environment()):
  296. """
  297. Perform model checking using hybrid engine.
  298. :param model: Model.
  299. :param property: Property to check for.
  300. :param only_initial_states: If True, only results for initial states are computed, otherwise for all states.
  301. :return: Model checking result.
  302. :rtype: CheckResult
  303. """
  304. if isinstance(property, Property):
  305. formula = property.raw_formula
  306. else:
  307. formula = property
  308. if model.supports_parameters:
  309. task = core.ParametricCheckTask(formula, only_initial_states)
  310. return core._parametric_model_checking_hybrid_engine(model, task, environment=environment)
  311. else:
  312. task = core.CheckTask(formula, only_initial_states)
  313. return core._model_checking_hybrid_engine(model, task, environment=environment)
  314. def transform_to_sparse_model(model):
  315. """
  316. Transform model in symbolic representation into model in sparse representation.
  317. :param model: Symbolic model.
  318. :return: Sparse model.
  319. """
  320. if model.supports_parameters:
  321. return core._transform_to_sparse_parametric_model(model)
  322. else:
  323. return core._transform_to_sparse_model(model)
  324. def transform_to_discrete_time_model(model, properties):
  325. """
  326. Transform continuous-time model to discrete time model.
  327. :param model: Continuous-time model.
  328. :param properties: List of properties to transform as well.
  329. :return: Tuple (Discrete-time model, converted properties).
  330. """
  331. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  332. if model.supports_parameters:
  333. return core._transform_to_discrete_time_parametric_model(model, formulae)
  334. else:
  335. return core._transform_to_discrete_time_model(model, formulae)
  336. def eliminate_non_markovian_chains(ma, properties, label_behavior):
  337. """
  338. Eliminate chains of non-Markovian states if possible.
  339. :param ma: Markov automaton.
  340. :param properties: List of properties to transform as well.
  341. :param label_behavior: Behavior of labels while elimination.
  342. :return: Tuple (converted MA, converted properties).
  343. """
  344. formulae = [(prop.raw_formula if isinstance(prop, Property) else prop) for prop in properties]
  345. if ma.supports_parameters:
  346. return core._eliminate_non_markovian_chains_parametric(ma, formulae, label_behavior)
  347. else:
  348. return core._eliminate_non_markovian_chains(ma, formulae, label_behavior)
  349. def prob01min_states(model, eventually_formula):
  350. assert type(eventually_formula) == logic.EventuallyFormula
  351. labelform = eventually_formula.subformula
  352. labelprop = core.Property("label-prop", labelform)
  353. phiStates = BitVector(model.nr_states, True)
  354. psiStates = model_checking(model, labelprop).get_truth_values()
  355. return compute_prob01min_states(model, phiStates, psiStates)
  356. def prob01max_states(model, eventually_formula):
  357. assert type(eventually_formula) == logic.EventuallyFormula
  358. labelform = eventually_formula.subformula
  359. labelprop = core.Property("label-prop", labelform)
  360. phiStates = BitVector(model.nr_states, True)
  361. psiStates = model_checking(model, labelprop).get_truth_values()
  362. return compute_prob01min_states(model, phiStates, psiStates)
  363. def compute_prob01_states(model, phi_states, psi_states):
  364. """
  365. Compute prob01 states for properties of the form phi_states until psi_states
  366. :param SparseDTMC model:
  367. :param BitVector phi_states:
  368. :param BitVector psi_states: Target states
  369. """
  370. if model.model_type != ModelType.DTMC:
  371. raise StormError("Prob 01 is only defined for DTMCs -- model must be a DTMC")
  372. if model.supports_parameters:
  373. return core._compute_prob01states_rationalfunc(model, phi_states, psi_states)
  374. else:
  375. return core._compute_prob01states_double(model, phi_states, psi_states)
  376. def compute_prob01min_states(model, phi_states, psi_states):
  377. if model.model_type == ModelType.DTMC:
  378. return compute_prob01_states(model, phi_states, psi_states)
  379. if model.supports_parameters:
  380. return core._compute_prob01states_min_rationalfunc(model, phi_states, psi_states)
  381. else:
  382. return core._compute_prob01states_min_double(model, phi_states, psi_states)
  383. def compute_prob01max_states(model, phi_states, psi_states):
  384. if model.model_type == ModelType.DTMC:
  385. return compute_prob01_states(model, phi_states, psi_states)
  386. if model.supports_parameters:
  387. return core._compute_prob01states_max_rationalfunc(model, phi_states, psi_states)
  388. else:
  389. return core._compute_prob01states_max_double(model, phi_states, psi_states)
  390. def topological_sort(model, forward=True, initial=[]):
  391. """
  392. :param model:
  393. :param forward:
  394. :return:
  395. """
  396. matrix = model.transition_matrix if forward else model.backward_transition_matrix
  397. if isinstance(model, storage._SparseParametricModel):
  398. return storage._topological_sort_rf(matrix, initial)
  399. elif isinstance(model, storage._SparseModel):
  400. return storage._topological_sort_double(matrix, initial)
  401. else:
  402. raise StormError("Unknown kind of model.")
  403. def construct_submodel(model, states, actions, keep_unreachable_states=True, options=SubsystemBuilderOptions()):
  404. """
  405. :param model: The model
  406. :param states: Which states should be preserved
  407. :param actions: Which actions should be preserved
  408. :param keep_unreachable_states: If False, run a reachability analysis.
  409. :return: A model with fewer states/actions
  410. """
  411. if isinstance(model, storage._SparseModel):
  412. return core._construct_subsystem_double(model, states, actions, keep_unreachable_states, options)
  413. else:
  414. raise NotImplementedError()
  415. def parse_properties(properties, context=None, filters=None):
  416. """
  417. :param properties: A string with the pctl properties
  418. :param context: A symbolic model that gives meaning to variables and constants.
  419. :param filters: filters, if applicable.
  420. :return: A list of properties
  421. """
  422. if context is None:
  423. return core.parse_properties_without_context(properties, filters)
  424. elif type(context) == core.SymbolicModelDescription:
  425. if context.is_prism_program():
  426. return core.parse_properties_for_prism_program(properties, context.as_prism_program(), filters)
  427. else:
  428. core.parse_properties_for_jani_program(properties, context.as_jani_model(), filters)
  429. elif type(context) == storage.PrismProgram:
  430. return core.parse_properties_for_prism_program(properties, context, filters)
  431. elif type(context) == storage.JaniModel:
  432. core.parse_properties_for_jani_model(properties, context, filters)
  433. else:
  434. raise StormError("Unclear context. Please pass a symbolic model description")
  435. def export_to_drn(model, file, options=DirectEncodingOptions()):
  436. """
  437. Export a model to DRN format
  438. :param model: The model
  439. :param file: A path
  440. :param options: DirectEncodingOptions
  441. :return:
  442. """
  443. if model.supports_parameters:
  444. return core._export_parametric_to_drn(model, file, options)
  445. if model.is_exact:
  446. return core._export_exact_to_drn(model, file, options)
  447. return core._export_to_drn(model, file, options)