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.
		
		
		
		
		
			
		
			
				
					
					
						
							210 lines
						
					
					
						
							9.0 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							210 lines
						
					
					
						
							9.0 KiB
						
					
					
				
								****************************
							 | 
						|
								Getting Started
							 | 
						|
								****************************
							 | 
						|
								
							 | 
						|
								Before starting with this guide, one should follow the instructions for :doc:`installation`.
							 | 
						|
								
							 | 
						|
								A Quick Tour through Stormpy
							 | 
						|
								================================
							 | 
						|
								
							 | 
						|
								This guide is intended for people which have a basic understanding of probabilistic models and their verification. More details and further pointers to literature can be found on the
							 | 
						|
								`Storm website <http://www.stormchecker.org/>`_.
							 | 
						|
								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.
							 | 
						|
								
							 | 
						|
								.. seealso:: The code examples are also given in the `examples/ <https://github.com/moves-rwth/stormpy/blob/master/examples/>`_ folder. These boxes throughout the text will tell you which example contains the code discussed.
							 | 
						|
								
							 | 
						|
								In order to do this, we import stormpy::
							 | 
						|
								
							 | 
						|
								    >>>	import stormpy
							 | 
						|
								    >>>	import stormpy.core
							 | 
						|
									
							 | 
						|
									
							 | 
						|
								Building models 
							 | 
						|
								------------------------------------------------
							 | 
						|
								.. seealso:: `01-getting-started.py <https://github.com/moves-rwth/stormpy/blob/master/examples/01-getting-started.py>`_
							 | 
						|
								
							 | 
						|
								There are several ways to create a Markov chain. 
							 | 
						|
								One of the easiest is to parse a description of such a Markov chain and to let Storm build the chain. 
							 | 
						|
								
							 | 
						|
								Here, we build a Markov chain from a prism program.
							 | 
						|
								Stormpy comes with a small set of examples, which we use here::
							 | 
						|
								
							 | 
						|
									>>> import stormpy.examples
							 | 
						|
									>>> import stormpy.examples.files
							 | 
						|
								
							 | 
						|
								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::
							 | 
						|
								
							 | 
						|
								    >>> model = stormpy.build_model(prism_program)
							 | 
						|
								    >>> print("Number of states: {}".format(model.nr_states))
							 | 
						|
								    Number of states: 13
							 | 
						|
								    >>> print("Number of transitions: {}".format(model.nr_transitions))
							 | 
						|
								    Number of transitions: 20
							 | 
						|
								    
							 | 
						|
								This tells us that the model has 13 states and 20 transitions.
							 | 
						|
								
							 | 
						|
								Moreover, initial states and deadlocks are indicated with a labelling function. We can see the labels present in the model by::
							 | 
						|
								
							 | 
						|
								    >>> 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`
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								Building properties
							 | 
						|
								--------------------------
							 | 
						|
								.. seealso:: `02-getting-started.py <https://github.com/moves-rwth/stormpy/blob/master/examples/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::
							 | 
						|
								
							 | 
						|
									P=? [F s=2]
							 | 
						|
								
							 | 
						|
								Stormpy can be used to parse this. As the variables in the property refer to a program, the program has to be passed as an additional parameter::
							 | 
						|
								
							 | 
						|
								    >>> formula_str = "P=? [F s=2]"
							 | 
						|
								    >>> properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program)
							 | 
						|
								
							 | 
						|
								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.
							 | 
						|
								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::
							 | 
						|
								
							 | 
						|
								    >>> model = stormpy.build_model(prism_program, properties)
							 | 
						|
								    >>> print("Labels in the model: {}".format(sorted(model.labeling.get_labels())))
							 | 
						|
								    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::
							 | 
						|
								
							 | 
						|
								    >>> print("Number of states: {}".format(model.nr_states))
							 | 
						|
								    Number of states: 8
							 | 
						|
									
							 | 
						|
								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.
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								Checking properties
							 | 
						|
								------------------------------------
							 | 
						|
								.. seealso:: `03-getting-started.py <https://github.com/moves-rwth/stormpy/blob/master/examples/03-getting-started.py>`_
							 | 
						|
								
							 | 
						|
								The last lesson taught us to construct properties and models with matching state labels. 
							 | 
						|
								Now default checking routines are just a simple command away::
							 | 
						|
								
							 | 
						|
								    >>> properties = stormpy.parse_properties_for_prism_program(formula_str, prism_program)
							 | 
						|
								    >>> model = stormpy.build_model(prism_program, properties)
							 | 
						|
								    >>> result = stormpy.model_checking(model, properties[0])
							 | 
						|
								    
							 | 
						|
								The result may contain information about all states.
							 | 
						|
								Instead, we can iterate over the results::
							 | 
						|
								
							 | 
						|
								    >>> assert result.result_for_all_states
							 | 
						|
								    >>> for x in result.get_values():
							 | 
						|
								    ...    pass # do something with x
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								.. topic:: Results for all states
							 | 
						|
								
							 | 
						|
								    Some model checking algorithms do not provide results for all states. In those cases, the result is not valid for all states, and to iterate over them, a different method is required. We will explain this later.
							 | 
						|
								
							 | 
						|
								A good way to get the result for the initial states is as follows::
							 | 
						|
								
							 | 
						|
								    >>> initial_state = model.initial_states[0]
							 | 
						|
								    >>> print(result.at(initial_state))
							 | 
						|
								    0.5
							 | 
						|
								
							 | 
						|
								Instantiating parametric models
							 | 
						|
								------------------------------------
							 | 
						|
								.. seealso:: `04-getting-started.py <https://github.com/moves-rwth/stormpy/blob/master/examples/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])
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								Checking parametric models
							 | 
						|
								------------------------------------
							 | 
						|
								.. seealso:: ``05-getting-started.py``
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								.. _getting-started-investigating-the-model:
							 | 
						|
								
							 | 
						|
								Investigating the model
							 | 
						|
								-------------------------------------
							 | 
						|
								.. seealso:: `06-getting-started.py <https://github.com/moves-rwth/stormpy/blob/master/examples/06-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::
							 | 
						|
								
							 | 
						|
								    >>> path = stormpy.examples.files.prism_dtmc_die
							 | 
						|
									>>> prism_program = stormpy.parse_prism_program(path)
							 | 
						|
								    >>> model = stormpy.build_model(prism_program)
							 | 
						|
								
							 | 
						|
								In this example, we will exploit this, and explore the underlying Markov chain of the model.
							 | 
						|
								The most basic question might be what the type of the constructed model is::
							 | 
						|
								
							 | 
						|
								    >>> print(model.model_type)
							 | 
						|
								    ModelType.DTMC
							 | 
						|
								
							 | 
						|
								We can also directly explore the underlying matrix.
							 | 
						|
								Notice that this code can be applied to both deterministic and non-deterministic models::
							 | 
						|
								
							 | 
						|
								    >>> for state in model.states:
							 | 
						|
								    ...    for action in state.actions:
							 | 
						|
								    ...        for transition in action.transitions:
							 | 
						|
								    ...            print("From state {}, with probability {}, go to state {}".format(state, transition.value(), transition.column))
							 | 
						|
								    From state 0, with probability 0.5, go to state 1
							 | 
						|
								    From state 0, with probability 0.5, go to state 2
							 | 
						|
								    From state 1, with probability 0.5, go to state 3
							 | 
						|
								    From state 1, with probability 0.5, go to state 4
							 | 
						|
								    From state 2, with probability 0.5, go to state 5
							 | 
						|
								    From state 2, with probability 0.5, go to state 6
							 | 
						|
								    From state 3, with probability 0.5, go to state 1
							 | 
						|
								    From state 3, with probability 0.5, go to state 7
							 | 
						|
								    From state 4, with probability 0.5, go to state 8
							 | 
						|
								    From state 4, with probability 0.5, go to state 9
							 | 
						|
								    From state 5, with probability 0.5, go to state 10
							 | 
						|
								    From state 5, with probability 0.5, go to state 11
							 | 
						|
								    From state 6, with probability 0.5, go to state 2
							 | 
						|
								    From state 6, with probability 0.5, go to state 12
							 | 
						|
								    From state 7, with probability 1.0, go to state 7
							 | 
						|
								    From state 8, with probability 1.0, go to state 8
							 | 
						|
								    From state 9, with probability 1.0, go to state 9
							 | 
						|
								    From state 10, with probability 1.0, go to state 10
							 | 
						|
								    From state 11, with probability 1.0, go to state 11
							 | 
						|
								    From state 12, with probability 1.0, go to state 12
							 | 
						|
								
							 | 
						|
								Let us go into some more details. For DTMCs, each state has (at most) one outgoing probability distribution.
							 | 
						|
								Thus::
							 | 
						|
								
							 | 
						|
								    >>> for state in model.states:
							 | 
						|
								    ...    assert len(state.actions) <= 1
							 |