Browse Source

Merge branch 'master' into almostsurepomdp

refactoring
Sebastian Junges 5 years ago
parent
commit
f009ed1f9f
  1. 8
      .travis.yml
  2. 15
      CMakeLists.txt
  3. 56
      cmake/CMakeLists.txt
  4. 3
      cmake/config.py.in
  5. 3
      doc/source/advanced_topics.rst
  6. 1
      doc/source/api.rst
  7. 7
      doc/source/api/gspn.rst
  8. 84
      doc/source/doc/gspns.rst
  9. 28
      examples/analysis/04-analysis.py
  10. 21
      examples/gspns/01-gspns.py
  11. 53
      examples/gspns/02-gspns.py
  12. 4
      lib/stormpy/examples/files.py
  13. 130
      lib/stormpy/examples/files/gspn/gspn_simple.pnml
  14. 16
      lib/stormpy/examples/files/gspn/gspn_simple.pnpro
  15. 7
      lib/stormpy/gspn/__init__.py
  16. 65
      lib/stormpy/storage/__init__.py
  17. 21
      setup.py
  18. 1
      setup/config.py
  19. 2
      src/dft/dft.cpp
  20. 2
      src/gspn/common.h
  21. 251
      src/gspn/gspn.cpp
  22. 7
      src/gspn/gspn.h
  23. 14
      src/mod_gspn.cpp
  24. 68
      src/storage/matrix.cpp
  25. 112
      src/storage/prism.cpp
  26. 11
      tests/configurations.py
  27. 2
      tests/dft/test_io.py
  28. 4
      tests/gspn/conftest.py
  29. 252
      tests/gspn/test_gspn.py
  30. 282
      tests/storage/test_matrix_builder.py
  31. 2
      tests/storage/test_state_generation.py
  32. 1
      travis/build-helper.sh

8
.travis.yml

@ -48,13 +48,13 @@ jobs:
# Docker Storm stable
- os: linux
compiler: gcc
env: TASK=Test CONFIG=Release DOCKER=storm:1.4.1 PYTHON=python3
env: TASK=Test CONFIG=Release DOCKER=storm:1.5.1 PYTHON=python3
script:
travis/build.sh
# Docker Storm stable in debug mode
- os: linux
compiler: gcc
env: TASK=Test CONFIG=Debug DOCKER=storm:1.4.1-debug PYTHON=python3
env: TASK=Test CONFIG=Debug DOCKER=storm:1.5.1-debug PYTHON=python3
script:
travis/build.sh
# Documentation
@ -76,6 +76,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.4.1 PYTHON=python3
env: TASK=Test CONFIG=Release DOCKER=storm:1.5.1 PYTHON=python3
- os: linux
env: TASK=Test CONFIG=Debug DOCKER=storm:1.4.1-debug PYTHON=python3
env: TASK=Test CONFIG=Debug DOCKER=storm:1.5.1-debug PYTHON=python3

15
CMakeLists.txt

@ -35,14 +35,17 @@ stormpy_module(logic)
stormpy_module(storage)
stormpy_module(utility)
if(HAVE_STORM_PARS)
stormpy_module(pars storm-pars "${storm-pars_INCLUDE_DIR}")
endif()
# Optional modules
if(HAVE_STORM_DFT)
stormpy_module(dft storm-dft "${storm-dft_INCLUDE_DIR}")
endif()
if(HAVE_STORM_GSPN)
stormpy_module(gspn storm-gspn "${storm-gspn_INCLUDE_DIR}")
endif()
if(HAVE_STORM_PARS)
stormpy_module(pars storm-pars "${storm-pars_INCLUDE_DIR}")
endif()
if(HAVE_STORM_POMDP)
stormpy_module(pomdp storm-pomdp "${storm-pomdp_INCLUDE_DIR}")
endif()
endif()

56
cmake/CMakeLists.txt

@ -8,13 +8,6 @@ set(STORM_DIR ${storm_DIR})
set(STORM_VERSION ${storm_VERSION})
set(STORM_LIBS ${storm_LIBRARIES})
# Check for storm-pars
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
find_library(STORM_DFT NAMES storm-dft HINTS "${storm_DIR}/lib/")
@ -24,6 +17,23 @@ else()
set(HAVE_STORM_DFT FALSE)
endif()
# Check for storm-gspn
find_library(STORM_GSPN NAMES storm-gspn HINTS "${storm_DIR}/lib/")
if(STORM_GSPN)
set(HAVE_STORM_GSPN TRUE)
else()
set(HAVE_STORM_GSPN FALSE)
endif()
# Check for storm-pars
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-pompd
find_library(STORM_POMDP NAMES storm-pomdp HINTS "${storm_DIR}/lib/")
if(STORM_POMDP)
set(HAVE_STORM_POMDP TRUE)
@ -31,10 +41,18 @@ else()
set(HAVE_STORM_POMDP FALSE)
endif()
if(HAVE_STORM_PARS)
set(HAVE_STORM_PARS_BOOL "True")
# Set variables
if(STORM_USE_CLN_EA)
set(STORM_CLN_EA_BOOL "True")
else()
set(HAVE_STORM_PARS_BOOL "False")
set(STORM_CLN_EA_BOOL "False")
endif()
if(STORM_USE_CLN_RF)
set(STORM_CLN_RF_BOOL "True")
else()
set(STORM_CLN_RF_BOOL "False")
endif()
if(HAVE_STORM_DFT)
@ -43,22 +61,22 @@ else()
set(HAVE_STORM_DFT_BOOL "False")
endif()
if(HAVE_STORM_POMDP)
set(HAVE_STORM_POMDP_BOOL "True")
if(HAVE_STORM_GSPN)
set(HAVE_STORM_GSPN_BOOL "True")
else()
set(HAVE_STORM_POMDP_BOOL "False")
set(HAVE_STORM_GSPN_BOOL "False")
endif()
if(STORM_USE_CLN_EA)
set(STORM_CLN_EA_BOOL "True")
if(HAVE_STORM_PARS)
set(HAVE_STORM_PARS_BOOL "True")
else()
set(STORM_CLN_EA_BOOL "False")
set(HAVE_STORM_PARS_BOOL "False")
endif()
if(STORM_USE_CLN_RF)
set(STORM_CLN_RF_BOOL "True")
if(HAVE_STORM_POMDP)
set(HAVE_STORM_POMDP_BOOL "True")
else()
set(STORM_CLN_RF_BOOL "False")
set(HAVE_STORM_POMDP_BOOL "False")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.py.in ${CMAKE_CURRENT_BINARY_DIR}/generated/config.py @ONLY)

3
cmake/config.py.in

@ -4,6 +4,7 @@ STORM_DIR = "@STORM_DIR@"
STORM_VERSION = "@STORM_VERSION@"
STORM_CLN_EA = @STORM_CLN_EA_BOOL@
STORM_CLN_RF = @STORM_CLN_RF_BOOL@
HAVE_STORM_PARS = @HAVE_STORM_PARS_BOOL@
HAVE_STORM_DFT = @HAVE_STORM_DFT_BOOL@
HAVE_STORM_GSPN = @HAVE_STORM_GSPN_BOOL@
HAVE_STORM_PARS = @HAVE_STORM_PARS_BOOL@
HAVE_STORM_POMDP = @HAVE_STORM_POMDP_BOOL@

3
doc/source/advanced_topics.rst

@ -16,4 +16,5 @@ This guide is a collection of examples meant to bridge the gap between the getti
doc/schedulers
doc/shortest_paths
doc/parametric_models
doc/dfts
doc/dfts
doc/gspns

1
doc/source/api.rst

@ -14,4 +14,5 @@ Work in progress!
api/utility
api/dft
api/gspn
api/pars

7
doc/source/api/gspn.rst

@ -0,0 +1,7 @@
Stormpy.gspn
**************************
.. automodule:: stormpy.gspn
:members:
:undoc-members:
:imported-members:

84
doc/source/doc/gspns.rst

@ -0,0 +1,84 @@
**********************************
Generalized Stochastic Petri Nets
**********************************
Loading GSPNs
==============
.. seealso:: `01-gspns.py <https://github.com/moves-rwth/stormpy/blob/master/examples/gspns/01-gspns.py>`_
Generalized stochastic Petri nets can be given either in the PNPRO format or in the PNML format.
We start by loading a GSPN stored in the PNML format::
>>> import stormpy
>>> import stormpy.gspn
>>> import stormpy.examples
>>> import stormpy.examples.files
>>> import_path = stormpy.examples.files.gspn_pnml_simple
>>> gspn_parser = stormpy.gspn.GSPNParser()
>>> gspn = gspn_parser.parse(import_path)
After loading, we can display some properties of the GSPN::
>>> print("Name of GSPN: {}.".format(gspn.get_name()))
Name of GSPN: simple_gspn.
>>> print("Number of places: {}.".format(gspn.get_number_of_places()))
Number of places: 4.
>>> print("Number of immediate transitions: {}.".format(gspn.get_number_of_immediate_transitions()))
Number of immediate transitions: 3.
>>> print("Number of timed transitions: {}.".format(gspn.get_number_of_timed_transitions()))
Number of timed transitions: 2.
Building GSPNs
=============================
.. seealso:: `02-gspns.py <https://github.com/moves-rwth/stormpy/blob/master/examples/gspns/02-gspns.py>`_
In the following, we describe how to construct GSPNs via the ``GSPNBuilder``.
First, we create an instance of the ``GSPNBuilder`` and set the name of the GSPN::
>>> builder = stormpy.gspn.GSPNBuilder()
>>> builder.set_name("my_gspn")
In the next step, we add an immediate transition to the GSPN.
Additionally, we define the position of the transition by setting its layout information::
>>> it_1 = builder.add_immediate_transition(1, 0.0, "it_1")
>>> it_layout = stormpy.gspn.LayoutInfo(1.5, 2.0)
>>> builder.set_transition_layout_info(it_1, it_layout)
We add a timed transition and set its layout information::
>>> tt_1 = builder.add_timed_transition(0, 0.4, "tt_1")
>>> tt_layout = stormpy.gspn.LayoutInfo(12.5, 2.0)
>>> builder.set_transition_layout_info(tt_1, tt_layout)
Next, we add two places to the GSPN and set their layouts::
>>> place_1 = builder.add_place(1, 1, "place_1")
>>> p1_layout = stormpy.gspn.LayoutInfo(6.5, 2.0)
>>> builder.set_place_layout_info(place_1, p1_layout)
>>> place_2 = builder.add_place(1, 0, "place_2")
>>> p2_layout = stormpy.gspn.LayoutInfo(18.5, 2.0)
>>> builder.set_place_layout_info(place_2, p2_layout)
Places and transitions can be linked by input, output and inhibition arcs.
We add the arcs of our GSPN as follows::
>>> builder.add_output_arc(it_1, place_1)
>>> builder.add_inhibition_arc(place_1, it_1)
>>> builder.add_input_arc(place_1, tt_1)
>>> builder.add_output_arc(tt_1, place_2)
We can now build the GSPN::
>>> gspn = builder.build_gspn()
After building, we export the GSPN.
GSPNs can be saved in the PNPRO format via ``export_gspn_pnpro_file(path)`` and in the PNML format via ``export_gspn_pnml_file(path)``.
We export the GSPN into the PNPRO format::
>>> export_path = stormpy.examples.files.gspn_pnpro_simple
>>> gspn.export_gspn_pnpro_file(export_path)

28
examples/analysis/04-analysis.py

@ -0,0 +1,28 @@
import stormpy
import stormpy.core
import stormpy.examples
import stormpy.examples.files
def example_analysis_04():
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(formula_str, prism_program)
options = stormpy.BuilderOptions([p.raw_formula for p in properties])
options.set_build_state_valuations()
model = stormpy.build_sparse_model_with_options(prism_program, options)
result = stormpy.model_checking(model, properties[0])
# Print the model checking result for all states
print("Model checking results:")
for i in range(len(model.states)):
print("\tstate #{}\t {}:\t {}".format(i,model.state_valuations.get_state(i),result.at(i)))
if __name__ == '__main__':
example_analysis_04()

21
examples/gspns/01-gspns.py

@ -0,0 +1,21 @@
import stormpy
import stormpy.gspn
import stormpy.examples
import stormpy.examples.files
def example_gspns_01():
# Load a GSPN from file (PNML format)
import_path = stormpy.examples.files.gspn_pnml_simple
gspn_parser = stormpy.gspn.GSPNParser()
gspn = gspn_parser.parse(import_path)
print("Name of GSPN: {}.".format(gspn.get_name()))
print("Number of places: {}.".format(gspn.get_number_of_places()))
print("Number of immediate transitions: {}.".format(gspn.get_number_of_immediate_transitions()))
print("Number of timed transitions: {}.".format(gspn.get_number_of_timed_transitions()))
if __name__ == '__main__':
example_gspns_01()

53
examples/gspns/02-gspns.py

@ -0,0 +1,53 @@
import stormpy
import stormpy.gspn
import stormpy.examples
import stormpy.examples.files
def example_gspns_02():
# Use GSPNBuilder to construct a GSPN
builder = stormpy.gspn.GSPNBuilder()
builder.set_name("my_gspn")
# Add immediate transition
it_1 = builder.add_immediate_transition(1, 0.0, "it_1")
it_layout = stormpy.gspn.LayoutInfo(1.5, 2.0)
builder.set_transition_layout_info(it_1, it_layout)
# Add timed transition
tt_1 = builder.add_timed_transition(0, 0.4, "tt_1")
tt_layout = stormpy.gspn.LayoutInfo(12.5, 2.0)
builder.set_transition_layout_info(tt_1, tt_layout)
# Add places
place_1 = builder.add_place(1, 1, "place_1")
p1_layout = stormpy.gspn.LayoutInfo(6.5, 2.0)
builder.set_place_layout_info(place_1, p1_layout)
place_2 = builder.add_place(1, 0, "place_2")
p2_layout = stormpy.gspn.LayoutInfo(18.5, 2.0)
builder.set_place_layout_info(place_2, p2_layout)
# Link places and transitions by arcs
builder.add_output_arc(it_1, place_1)
builder.add_inhibition_arc(place_1, it_1)
builder.add_input_arc(place_1, tt_1)
builder.add_output_arc(tt_1, place_2)
# Build GSPN
gspn = builder.build_gspn()
print("Name of GSPN: {}.".format(gspn.get_name()))
print("Number of places: {}.".format(gspn.get_number_of_places()))
print("Number of immediate transitions: {}.".format(gspn.get_number_of_immediate_transitions()))
print("Number of timed transitions: {}.".format(gspn.get_number_of_timed_transitions()))
# Export to file (PNPRO format)
export_path = stormpy.examples.files.gspn_pnpro_simple
gspn.export_gspn_pnpro_file(export_path)
if __name__ == '__main__':
example_gspns_02()

4
lib/stormpy/examples/files.py

@ -41,3 +41,7 @@ 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)"""
gspn_pnpro_simple = _path("gspn", "gspn_simple.pnpro")
"""GSPN example (PNPRO format)"""
gspn_pnml_simple = _path("gspn", "gspn_simple.pnml")
"""GSPN example (PNML format)"""

130
lib/stormpy/examples/files/gspn/gspn_simple.pnml

@ -0,0 +1,130 @@
<pnml>
<net id="simple_gspn">
<place id="place_1">
<initialMarking>
<value>Default,1</value>
</initialMarking>
<capacity>
<value>Default,1</value>
</capacity>
</place>
<place id="place_2">
<initialMarking>
<value>Default,0</value>
</initialMarking>
<capacity>
<value>Default,1</value>
</capacity>
</place>
<place id="place_3">
<initialMarking>
<value>Default,0</value>
</initialMarking>
<capacity>
<value>Default,1</value>
</capacity>
</place>
<place id="place_4">
<initialMarking>
<value>Default,0</value>
</initialMarking>
<capacity>
<value>Default,1</value>
</capacity>
</place>
<transition id="it_1">
<rate>
<value>1</value>
</rate>
<timed>
<value>false</value>
</timed>
</transition>
<transition id="it_2">
<rate>
<value>1</value>
</rate>
<timed>
<value>false</value>
</timed>
</transition>
<transition id="it_3">
<rate>
<value>1</value>
</rate>
<timed>
<value>false</value>
</timed>
</transition>
<transition id="tt_1">
<rate>
<value>0.4</value>
</rate>
<timed>
<value>true</value>
</timed>
</transition>
<transition id="tt_2">
<rate>
<value>0.4</value>
</rate>
<timed>
<value>true</value>
</timed>
</transition>
<arc id="arc0" source="place_1" target="it_1" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="inhibition" />
</arc>
<arc id="arc1" source="it_1" target="place_1" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc2" source="place_2" target="it_2" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc3" source="it_2" target="place_3" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc4" source="place_4" target="it_3" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc5" source="place_1" target="tt_1" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc6" source="tt_1" target="place_2" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc7" source="place_3" target="tt_2" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
<arc id="arc8" source="tt_2" target="place_4" >
<inscription>
<value>Default,1</value>
</inscription>
<type value="normal" />
</arc>
</net>
</pnml>

16
lib/stormpy/examples/files/gspn/gspn_simple.pnpro

@ -0,0 +1,16 @@
<project name="storm-export" version="121">
<gspn name="my_gspn" >
<nodes>
<place marking="1" name ="place_1" x="6.5" y="2" />
<place marking="0" name ="place_2" x="18.5" y="2" />
<transition name="tt_1" type="EXP" nservers="1" delay="0.4000000000" x="12.50000000" y="2.000000000" />
<transition name="it_1" type="IMM" priority="1" weight="1.000000000" x="1.500000000" y="2.000000000" />
</nodes>
<edges>
<arc head="tt_1" tail="place_1" kind="INPUT" mult="1" />
<arc head="place_2" tail="tt_1" kind="OUTPUT" mult="1" />
<arc head="it_1" tail="place_1" kind="INHIBITOR" mult="1" />
<arc head="place_1" tail="it_1" kind="OUTPUT" mult="1" />
</edges>
</gspn>
</project>

7
lib/stormpy/gspn/__init__.py

@ -0,0 +1,7 @@
from . import _config
if not _config.storm_with_gspn:
raise ImportError("No support for GSPNs was built in Storm.")
from . import gspn
from .gspn import *

65
lib/stormpy/storage/__init__.py

@ -1,4 +1,67 @@
import stormpy.utility
from . import storage
from .storage import *
def build_sparse_matrix(array, row_group_indices=[]):
"""
Build a sparse matrix from numpy array.
:param numpy array: The array.
:param List[double] row_group_indices: List containing the starting row of each row group in ascending order.
:return: Sparse matrix.
"""
num_row = array.shape[0]
num_col = array.shape[1]
len_group_indices = len(row_group_indices)
if len_group_indices > 0:
builder = storage.SparseMatrixBuilder(rows=num_row, columns=num_col, has_custom_row_grouping=True,
row_groups=len_group_indices)
else:
builder = storage.SparseMatrixBuilder(rows=num_row, columns=num_col)
row_group_index = 0
for r in range(num_row):
# check whether to start a custom row group
if row_group_index < len_group_indices and r == row_group_indices[row_group_index]:
builder.new_row_group(r)
row_group_index += 1
# insert values of the current row
for c in range(num_col):
builder.add_next_value(r, c, array[r, c])
return builder.build()
def build_parametric_sparse_matrix(array, row_group_indices=[]):
"""
Build a sparse matrix from numpy array.
:param numpy array: The array.
:param List[double] row_group_indices: List containing the starting row of each row group in ascending order.
:return: Parametric sparse matrix.
"""
num_row = array.shape[0]
num_col = array.shape[1]
len_group_indices = len(row_group_indices)
if len_group_indices > 0:
builder = storage.ParametricSparseMatrixBuilder(rows=num_row, columns=num_col, has_custom_row_grouping=True,
row_groups=len_group_indices)
else:
builder = storage.ParametricSparseMatrixBuilder(rows=num_row, columns=num_col)
row_group_index = 0
for r in range(num_row):
# check whether to start a custom row group
if row_group_index < len_group_indices and r == row_group_indices[row_group_index]:
builder.new_row_group(r)
row_group_index += 1
# insert values of the current row
for c in range(num_col):
builder.add_next_value(r, c, array[r, c])
return builder.build()

21
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.4.2"
storm_min_version = "1.5.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:
@ -32,6 +32,7 @@ class CMakeBuild(build_ext):
user_options = build_ext.user_options + [
('storm-dir=', None, 'Path to storm root (binary) location'),
('disable-dft', None, 'Disable support for DFTs'),
('disable-gspn', None, 'Disable support for GSPNs'),
('disable-pars', None, 'Disable support for parametric models'),
('disable-pomdp', None, 'Disable support for POMDP analysis'),
('debug', None, 'Build in Debug mode'),
@ -84,6 +85,7 @@ class CMakeBuild(build_ext):
# Check additional support
use_dft = cmake_conf.HAVE_STORM_DFT and not self.config.get_as_bool("disable_dft")
use_gspn = cmake_conf.HAVE_STORM_GSPN and not self.config.get_as_bool("disable_gspn")
use_pars = cmake_conf.HAVE_STORM_PARS and not self.config.get_as_bool("disable_pars")
use_pomdp = cmake_conf.HAVE_STORM_POMDP #and not self.config.get_as_bool("disable_pomdp")
@ -93,6 +95,10 @@ class CMakeBuild(build_ext):
print("Stormpy - Support for DFTs found and included.")
else:
print("Stormpy - Warning: No support for DFTs!")
if use_gspn:
print("Stormpy - Support for GSPNs found and included.")
else:
print("Stormpy - Warning: No support for GSPNs!")
if use_pars:
print("Stormpy - Support for parametric models found and included.")
else:
@ -110,6 +116,8 @@ class CMakeBuild(build_ext):
cmake_args += ['-Dstorm_DIR=' + storm_dir]
if use_dft:
cmake_args += ['-DHAVE_STORM_DFT=ON']
if use_gspn:
cmake_args += ['-DHAVE_STORM_GSPN=ON']
if use_pars:
cmake_args += ['-DHAVE_STORM_PARS=ON']
if use_pomdp:
@ -146,6 +154,7 @@ class CMakeBuild(build_ext):
f.write("FactorizedRationalFunction = pycarl.{}.FactorizedRationalFunction\n".format(rfpackage))
f.write("\n")
f.write("storm_with_dft = {}\n".format(use_dft))
f.write("storm_with_gspn = {}\n".format(use_gspn))
f.write("storm_with_pars = {}\n".format(use_pars))
elif ext.name == "info":
@ -161,6 +170,13 @@ class CMakeBuild(build_ext):
if not use_dft:
print("Stormpy - DFT bindings skipped")
continue
elif ext.name == "gspn":
with open(os.path.join(self._extdir(ext.name), ext.subdir, "_config.py"), "w") as f:
f.write("# Generated from setup.py at {}\n".format(datetime.datetime.now()))
f.write("storm_with_gspn = {}".format(use_gspn))
if not use_gspn:
print("Stormpy - GSPN bindings skipped")
continue
elif ext.name == "pars":
with open(os.path.join(self._extdir(ext.name), ext.subdir, "_config.py"), "w") as f:
f.write("# Generated from setup.py at {}\n".format(datetime.datetime.now()))
@ -182,6 +198,7 @@ class CMakeBuild(build_ext):
# Set default values for custom cmdline flags
self.storm_dir = None
self.disable_dft = None
self.disable_gspn = None
self.disable_pars = None
self.disable_pomdp = None
self.debug = None
@ -195,6 +212,7 @@ class CMakeBuild(build_ext):
# Update setup config
self.config.update("storm_dir", self.storm_dir)
self.config.update("disable_dft", self.disable_dft)
self.config.update("disable_gspn", self.disable_gspn)
self.config.update("disable_pars", self.disable_pars)
self.config.update("disable_pomdp", self.disable_pomdp)
self.config.update("debug", self.debug)
@ -248,6 +266,7 @@ setup(
CMakeExtension('storage', subdir='storage'),
CMakeExtension('utility', subdir='utility'),
CMakeExtension('dft', subdir='dft'),
CMakeExtension('gspn', subdir='gspn'),
CMakeExtension('pars', subdir='pars'),
CMakeExtension('pomdp', subdir='pomdp')],

1
setup/config.py

@ -29,6 +29,7 @@ class SetupConfig:
return {
"storm_dir": "",
"disable_dft": False,
"disable_gspn": False,
"disable_pars": False,
"debug": False,
"jobs": str(no_jobs),

2
src/dft/dft.cpp

@ -3,6 +3,7 @@
#include "storm-dft/storage/dft/DFT.h"
#include "storm/settings/SettingsManager.h"
#include "storm-dft/settings/modules/FaultTreeSettings.h"
#include "storm-dft/settings/modules/DftIOSettings.h"
template<typename ValueType> using DFT = storm::storage::DFT<ValueType>;
@ -12,6 +13,7 @@ void define_dft(py::module& m) {
m.def("_set_up", []() {
storm::settings::addModule<storm::settings::modules::FaultTreeSettings>();
storm::settings::addModule<storm::settings::modules::DftIOSettings>();
}, "Initialize Storm-dft");

2
src/gspn/common.h

@ -0,0 +1,2 @@
#include "src/common.h"
#include "storm-gspn/api/storm-gspn.h"

251
src/gspn/gspn.cpp

@ -0,0 +1,251 @@
#include "gspn.h"
#include "src/helpers.h"
#include "storm-gspn/storage/gspn/GSPN.h"
#include "storm-gspn/storage/gspn/GspnBuilder.h"
#include "storm/settings/SettingsManager.h"
#include "storm-gspn/parser/GspnParser.h"
#include "storm/utility/file.h"
using GSPN = storm::gspn::GSPN;
using GSPNBuilder = storm::gspn::GspnBuilder;
using LayoutInfo = storm::gspn::LayoutInfo;
using Place = storm::gspn::Place;
using TimedTransition = storm::gspn::TimedTransition<GSPN::RateType>;
using ImmediateTransition = storm::gspn::ImmediateTransition<GSPN::WeightType>;
using Transition = storm::gspn::Transition;
using TransitionPartition = storm::gspn::TransitionPartition;
using GSPNParser = storm::parser::GspnParser;
void gspnToFile(GSPN const& gspn, std::string const& filepath, bool toPnpro) {
std::ofstream fs;
storm::utility::openFile(filepath, fs);
if(toPnpro) {
gspn.toPnpro(fs);
}
else {
gspn.toPnml(fs);
}
storm::utility::closeFile(fs);
}
void define_gspn(py::module& m) {
// GSPN_Builder class
py::class_<GSPNBuilder, std::shared_ptr<GSPNBuilder>>(m, "GSPNBuilder", "Generalized Stochastic Petri Net Builder")
.def(py::init(), "Constructor")
.def("set_name", &GSPNBuilder::setGspnName, "Set name of GSPN", "name"_a)
.def("add_place", &GSPNBuilder::addPlace, "Add a place to the GSPN", py::arg("capacity") = 1, py::arg("initial_tokens") = 0, py::arg("name") = "")
.def("set_place_layout_info", &GSPNBuilder::setPlaceLayoutInfo, py::arg("place_id"), py::arg("layout_info"), R"doc(
Set place layout information.
:param uint64_t id: The ID of the place.
:param stormpy.LayoutInfo layout_info: The layout information.
)doc")
.def("add_immediate_transition", &GSPNBuilder::addImmediateTransition, "Add an immediate transition to the GSPN", "priority"_a = 0, "weight"_a = 0, "name"_a = "")
.def("add_timed_transition", py::overload_cast<uint_fast64_t const&, double const& , std::string const&>(&GSPNBuilder::addTimedTransition), "Add a timed transition to the GSPN", "priority"_a, "rate"_a, "name"_a = "")
.def("add_timed_transition", py::overload_cast<uint_fast64_t const&, double const& , boost::optional<uint64_t> const&, std::string const&>(&GSPNBuilder::addTimedTransition), "priority"_a, "rate"_a, "num_servers"_a, "name"_a = "")
.def("set_transition_layout_info", &GSPNBuilder::setTransitionLayoutInfo, "transition_id"_a, "layout_info"_a, R"doc(
Set transition layout information.
:param uint64_t id: The ID of the transition.
:param stormpy.LayoutInfo layout_info: The layout information.
)doc")
.def("add_input_arc", py::overload_cast<uint_fast64_t const&, uint_fast64_t const&, uint_fast64_t const&>(&GSPNBuilder::addInputArc), "from"_a , "to"_a, "multiplicity"_a = 1, R"doc(
Add a new input arc from a place to a transition
:param from: The ID or name of the place from which the arc is originating.
:type from: uint_64_t or str
:param uint_64_t to: The ID or name of the transition to which the arc goes to.
:type from: uint_64_t or str
:param uint64_t multiplicity: The multiplicity of the arc, default = 1.
)doc")
.def("add_input_arc", py::overload_cast<std::string const&, std::string const&, uint64_t>(&GSPNBuilder::addInputArc), "from"_a, "to"_a, "multiplicity"_a = 1)
.def("add_inhibition_arc", py::overload_cast<uint_fast64_t const&, uint_fast64_t const&, uint_fast64_t const&>(&GSPNBuilder::addInhibitionArc), "from"_a, "to"_a, "multiplicity"_a = 1, R"doc(
Add an new inhibition arc from a place to a transition.
:param from: The ID or name of the place from which the arc is originating.
:type from: uint_64_t or str
:param to: The ID or name of the transition to which the arc goes to.
:type to: uint_64_t or str
:param uint64_t multiplicity: The multiplicity of the arc, default = 1.
)doc")
.def("add_inhibition_arc", py::overload_cast<std::string const&, std::string const&, uint64_t>(&GSPNBuilder::addInhibitionArc), "from"_a, "to"_a, "multiplicity"_a = 1)
.def("add_output_arc", py::overload_cast<uint_fast64_t const&, uint_fast64_t const&, uint_fast64_t const&>(&GSPNBuilder::addOutputArc), "from"_a, "to"_a, "multiplicity"_a = 1, R"doc(
Add an new output arc from a transition to a place.
:param from: The ID or name of the transition from which the arc is originating.
:type from: uint_64_t or str
:param to: The ID or name of the place to which the arc goes to.
:type to: uint_64_t or str
:param uint64_t multiplicity: The multiplicity of the arc, default = 1.
)doc")
.def("add_output_arc", py::overload_cast<std::string const&, std::string const&, uint64_t>(&GSPNBuilder::addOutputArc), "from"_a, "to"_a, "multiplicity"_a)
.def("add_normal_arc", &GSPNBuilder::addNormalArc, "from"_a, "to"_a, "multiplicity"_a = 1, R"doc(
Add an arc from a named element to a named element.
Can be both input or output arc, but not an inhibition arc.
Convenience function for textual format parsers.
:param str from: Source element in the GSPN from where this arc starts.
:param str to: Target element in the GSPN where this arc ends.
:param uint64_t multiplicity: Multiplicity of the arc, default = 1.
)doc")
.def("build_gspn", &GSPNBuilder::buildGspn, "Construct GSPN", "expression_manager"_a = nullptr, "constants_substitution"_a = std::map<storm::expressions::Variable, storm::expressions::Expression>())
;
// GspnParser class
py::class_<GSPNParser, std::shared_ptr<GSPNParser>>(m, "GSPNParser")
.def(py::init<>())
.def("parse", [](GSPNParser& p, std::string const& filename, std::string const& constantDefinitions) -> GSPN& {return *(p.parse(filename,constantDefinitions)); }, "filename"_a, "constant_definitions"_a = "")
;
// GSPN class
py::class_<GSPN, std::shared_ptr<GSPN>>(m, "GSPN", "Generalized Stochastic Petri Net")
// Constructor
.def(py::init<std::string const&, std::vector<Place> const&, std::vector<ImmediateTransition> const&,
std::vector<TimedTransition> const&, std::vector<TransitionPartition> const&, std::shared_ptr<storm::expressions::ExpressionManager> const&, std::map<storm::expressions::Variable,
storm::expressions::Expression> const&>(), "name"_a, "places"_a, "immediate_transitions"_a, "timed_transitions"_a, "partitions"_a, "expression_manager"_a, "constants_substitution"_a = std::map<storm::expressions::Variable, storm::expressions::Expression>())
.def("get_name", &GSPN::getName, "Get name of GSPN")
.def("set_name", &GSPN::setName, "Set name of GSPN")
.def("get_number_of_places", &GSPN::getNumberOfPlaces, "Get the number of places in this GSPN")
.def("get_number_of_immediate_transitions", &GSPN::getNumberOfImmediateTransitions, "Get the number of immediate transitions in this GSPN")
.def("get_number_of_timed_transitions", &GSPN::getNumberOfTimedTransitions, "Get the number of timed transitions in this GSPN")
.def("get_place", [](GSPN const& g, uint64_t id) -> const Place& {return *(g.getPlace(id)); }, "id"_a, R"doc(
Returns the place with the corresponding id.
:param uint64_t id: The ID of the place.
:rtype: stormpy.Place
)doc")
.def("get_place", [](GSPN const& g, std::string const& name) -> const Place& {return *(g.getPlace(name)); }, "name"_a)
.def("get_timed_transition", [](GSPN const& g, std::string const& name) -> const TimedTransition& {return *(g.getTimedTransition(name)); }, "name"_a, "Returns the timed transition with the corresponding name")
.def("get_immediate_transition", [](GSPN const& g, std::string const& name) -> const ImmediateTransition& {return *(g.getImmediateTransition(name)); }, "name"_a, "Returns the immediate transition with the corresponding name")
.def("get_transition",[](GSPN const& g, std::string const& name) -> const Transition& {return *(g.getTransition(name)); }, "name"_a, "Returns the transition with the corresponding name")
.def("get_partitions", &GSPN::getPartitions, "Get the partitions of this GSPN")
.def("get_places", &GSPN::getPlaces, R"doc(
Returns a vector of the places of this GSPN.
:rtype: list[stormpy.Place]
)doc")
.def("get_timed_transitions", &GSPN::getTimedTransitions, R"doc(
Returns a vector of the timed transitions of this GSPN.
:rtype: list[stormpy.TimedTransition]
)doc")
.def("get_immediate_transitions", &GSPN::getImmediateTransitions, R"doc(
Returns the immediate transitions of this GSPN.
:rtype: list[stormpy.ImmediateTransition]
)doc")
.def("get_initial_marking", &GSPN::getInitialMarking, "Computes the initial marking of this GSPN")
.def("is_valid", &GSPN::isValid, "Perform some checks")
// GSPN export
.def("export_gspn_pnpro_file", [](GSPN& g, std::string const& filepath) -> void { gspnToFile(g, filepath, true); }, "filepath"_a, "Export GSPN to PNPRO file")
.def("export_gspn_pnml_file", [](GSPN& g, std::string const& filepath) -> void { gspnToFile(g, filepath, false); }, "filepath"_a, "Export GSPN to PNML file")
.def_static("timed_transition_id_to_transition_id", &GSPN::timedTransitionIdToTransitionId)
.def_static("immediate_transition_id_to_transition_id", &GSPN::immediateTransitionIdToTransitionId)
.def_static("transition_id_to_timed_transition_id", &GSPN::transitionIdToTimedTransitionId)
.def_static("transition_id_to_immediate_transition_id", &GSPN::transitionIdToImmediateTransitionId)
;
// LayoutInfo class
py::class_<LayoutInfo>(m, "LayoutInfo")
.def(py::init<>())
.def(py::init<double, double, double>(), "x"_a, "y"_a, "rotation"_a = 0.0)
.def_readwrite("x", &LayoutInfo::x)
.def_readwrite("y", &LayoutInfo::y)
.def_readwrite("rotation", &LayoutInfo::rotation)
;
// Place class
py::class_<Place, std::shared_ptr<Place>>(m, "Place", "Place in a GSPN")
.def(py::init<uint64_t>(), "id"_a)
.def("get_name", &Place::getName, "Get name of this place")
.def("set_name", &Place::setName, "name"_a, "Set name of this place")
.def("get_id", &Place::getID, "Get the id of this place")
.def("set_number_of_initial_tokens", &Place::setNumberOfInitialTokens, "tokens"_a, "Set the number of initial tokens of this place")
.def("get_number_of_initial_tokens", &Place::getNumberOfInitialTokens, "Get the number of initial tokens of this place")
.def("set_capacity", &Place::setCapacity, "cap"_a, "Set the capacity of tokens of this place") // problem: boost or lambda [](..
.def("get_capacity", &Place::getCapacity, "Get the capacity of tokens of this place")
.def("has_restricted_capacity", &Place::hasRestrictedCapacity, "Is capacity of this place restricted")
;
// Transition class
py::class_<Transition, std::shared_ptr<Transition>>(m, "Transition", "Transition in a GSPN")
.def(py::init<>())
.def("get_id", &Transition::getID, "Get id of this transition")
.def("set_name", &Transition::setName, "name"_a, "Set name of this transition")
.def("get_name", &Transition::getName, "Get name of this transition")
.def("set_priority", &Transition::setPriority, "priority"_a, "Set priority of this transition")
.def("get_priority", &Transition::getPriority, "Get priority of this transition")
.def("set_input_arc_multiplicity", &Transition::setInputArcMultiplicity, "place"_a, "multiplicity"_a, "Set the multiplicity of the input arc originating from the place.")
.def("remove_input_arc", &Transition::removeInputArc, "place"_a, "Remove an input arc connected to a given place.")
.def("exists_input_arc", &Transition::existsInputArc, "place"_a, "Check whether the given place is connected to this transition via an input arc.")
.def("set_output_arc_multiplicity", &Transition::setOutputArcMultiplicity, "place"_a, "multiplicity"_a, "Set the multiplicity of the output arc going to the place.")
.def("remove_output_arc", &Transition::removeOutputArc, "place"_a, "Remove an output arc connected to a given place.")
.def("exists_output_arc", &Transition::existsOutputArc, "place"_a, "Check whether the given place is connected to this transition via an output arc.")
.def("set_inhibition_arc_multiplicity", &Transition::setInhibitionArcMultiplicity, "place"_a, "multiplicity"_a, "Set the multiplicity of the inhibition arc originating from the place.")
.def("remove_inhibition_arc", &Transition::removeInhibitionArc, "place"_a, "Remove an inhibition arc connected to a given place.")
.def("exists_inhibition_arc", &Transition::existsInhibitionArc, "place"_a, "Check whether the given place is connected to this transition via an inhibition arc.")
.def("get_input_places", &Transition::getInputPlaces)
.def("get_output_places", &Transition::getOutputPlaces)
.def("get_inhibition_places", &Transition::getInhibitionPlaces)
.def("get_input_arc_multiplicity", &Transition::getInputArcMultiplicity, "place"_a, "Returns the corresponding multiplicity.")
.def("get_inhibition_arc_multiplicity", &Transition::getInhibitionArcMultiplicity, "place"_a, "Returns the corresponding multiplicity.")
.def("get_output_arc_multiplicity", &Transition::getOutputArcMultiplicity, "place"_a, "Returns the corresponding multiplicity.")
.def("is_enabled", &Transition::isEnabled, "marking"_a, "Check if the given marking enables the transition.")
.def("fire", &Transition::fire, "marking"_a, "Fire the transition if possible.")
;
// TimedTransition class
py::class_<TimedTransition, Transition, std::shared_ptr<TimedTransition>>(m, "TimedTransition", "TimedTransition in a GSPN")
.def(py::init<>())
.def("get_rate", &TimedTransition::getRate, "Get rate of this transition")
.def("set_rate", &TimedTransition::setRate, "rate"_a, "Set rate of this transition")
.def("set_k_server_semantics", &TimedTransition::setKServerSemantics, "k"_a, "Set semantics of this transition")
.def("set_single_server_semantics", &TimedTransition::setSingleServerSemantics, "Set semantics of this transition")
.def("set_infinite_server_semantics", &TimedTransition::setInfiniteServerSemantics, "Set semantics of this transition")
.def("has_k_server_semantics", &TimedTransition::hasKServerSemantics, "Get semantics of this transition")
.def("has_single_server_semantics", &TimedTransition::hasSingleServerSemantics, "Get semantics of this transition")
.def("has_infinite_server_semantics", &TimedTransition::hasInfiniteServerSemantics, "Get semantics of this transition")
.def("get_number_of_servers", &TimedTransition::getNumberOfServers, "Get number of servers")
;
// ImmediateTransition class
py::class_<ImmediateTransition, Transition, std::shared_ptr<ImmediateTransition>>(m, "ImmediateTransition", "ImmediateTransition in a GSPN")
.def(py::init<>())
.def("get_weight", &ImmediateTransition::getWeight, "Get weight of this transition")
.def("set_weight", &ImmediateTransition::setWeight, "weight"_a, "Set weight of this transition")
.def("no_weight_attached", &ImmediateTransition::noWeightAttached, "True iff no weight is attached")
;
// TransitionPartition class
py::class_<TransitionPartition>(m, "TransitionPartition")
.def(py::init<>())
.def_readwrite("priority", &TransitionPartition::priority)
.def_readwrite("transitions", &TransitionPartition::transitions)
.def("nr_transitions", &TransitionPartition::nrTransitions, "Get number of transitions")
;
}

7
src/gspn/gspn.h

@ -0,0 +1,7 @@
#pragma once
#include "common.h"
void define_gspn(py::module& m);

14
src/mod_gspn.cpp

@ -0,0 +1,14 @@
#include "common.h"
#include "gspn/gspn.h"
PYBIND11_MODULE(gspn, m) {
m.doc() = "Support for GSPNs";
#ifdef STORMPY_DISABLE_SIGNATURE_DOC
py::options options;
options.disable_function_signatures();
#endif
define_gspn(m);
}

68
src/storage/matrix.cpp

@ -6,6 +6,7 @@
#include "src/helpers.h"
template<typename ValueType> using SparseMatrix = storm::storage::SparseMatrix<ValueType>;
template<typename ValueType> using SparseMatrixBuilder = storm::storage::SparseMatrixBuilder<ValueType>;
template<typename ValueType> using entry_index = typename storm::storage::SparseMatrix<ValueType>::index_type;
template<typename ValueType> using MatrixEntry = storm::storage::MatrixEntry<entry_index<ValueType>, ValueType>;
using RationalFunction = storm::RationalFunction;
@ -30,6 +31,73 @@ void define_sparse_matrix(py::module& m) {
.def_property_readonly("column", &MatrixEntry<RationalFunction>::getColumn, "Column")
;
// SparseMatrixBuilder
py::class_<SparseMatrixBuilder<double>>(m, "SparseMatrixBuilder", "Builder of sparse matrix")
.def(py::init<double, double, double, bool, bool, double>(), "rows"_a = 0, "columns"_a = 0, "entries"_a = 0, "force_dimensions"_a = true, "has_custom_row_grouping"_a = false, "row_groups"_a = 0)
.def("add_next_value", &SparseMatrixBuilder<double>::addNextValue, R"dox(
Sets the matrix entry at the given row and column to the given value. After all entries have been added,
calling function build() is mandatory.
Note: this is a linear setter. That is, it must be called consecutively for each entry, row by row and
column by column. As multiple entries per column are admitted, consecutive calls to this method are
admitted to mention the same row-column-pair. If rows are skipped entirely, the corresponding rows are
treated as empty. If these constraints are not met, an exception is thrown.
:param double row: The row in which the matrix entry is to be set
:param double column: The column in which the matrix entry is to be set
:param double value: The value that is to be set at the specified row and column
)dox", py::arg("row"), py::arg("column"), py::arg("value"))
.def("new_row_group", &SparseMatrixBuilder<double>::newRowGroup, py::arg("starting_row"), "Start a new row group in the matrix")
.def("build", &SparseMatrixBuilder<double>::build, py::arg("overridden_row_count") = 0, py::arg("overridden_column_count") = 0, py::arg("overridden-row_group_count") = 0, "Finalize the sparse matrix")
.def("get_last_row", &SparseMatrixBuilder<double>::getLastRow, "Get the most recently used row")
.def("get_current_row_group_count", &SparseMatrixBuilder<double>::getCurrentRowGroupCount, "Get the current row group count")
.def("get_last_column", &SparseMatrixBuilder<double>::getLastColumn, "the most recently used column")
.def("replace_columns", &SparseMatrixBuilder<double>::replaceColumns, R"dox(
Replaces all columns with id >= offset according to replacements.
Every state with id offset+i is replaced by the id in replacements[i]. Afterwards the columns are sorted.
:param std::vector<double> const& replacements: replacements Mapping indicating the replacements from offset+i -> value of i
:param int offset: Offset to add to each id in vector index.
)dox", py::arg("replacements"), py::arg("offset"))
;
py::class_<SparseMatrixBuilder<RationalFunction>>(m, "ParametricSparseMatrixBuilder", "Builder of parametric sparse matrix")
.def(py::init<double, double, double, bool, bool, double>(), "rows"_a = 0, "columns"_a = 0, "entries"_a = 0, "force_dimensions"_a = true, "has_custom_row_grouping"_a = false, "row_groups"_a = 0)
.def("add_next_value", &SparseMatrixBuilder<RationalFunction>::addNextValue, R"dox(
Sets the matrix entry at the given row and column to the given value. After all entries have been added,
calling function build() is mandatory.
Note: this is a linear setter. That is, it must be called consecutively for each entry, row by row and
column by column. As multiple entries per column are admitted, consecutive calls to this method are
admitted to mention the same row-column-pair. If rows are skipped entirely, the corresponding rows are
treated as empty. If these constraints are not met, an exception is thrown.
:param double row: The row in which the matrix entry is to be set
:param double column: The column in which the matrix entry is to be set
:param RationalFunction value: The value that is to be set at the specified row and column
)dox", py::arg("row"), py::arg("column"), py::arg("value"))
.def("new_row_group", &SparseMatrixBuilder<RationalFunction>::newRowGroup, py::arg("starting_row"), "Start a new row group in the matrix")
.def("build", &SparseMatrixBuilder<RationalFunction>::build, py::arg("overridden_row_count") = 0, py::arg("overridden_column_count") = 0, py::arg("overridden-row_group_count") = 0, "Finalize the sparse matrix")
.def("get_last_row", &SparseMatrixBuilder<RationalFunction>::getLastRow, "Get the most recently used row")
.def("get_current_row_group_count", &SparseMatrixBuilder<RationalFunction>::getCurrentRowGroupCount, "Get the current row group count")
.def("get_last_column", &SparseMatrixBuilder<RationalFunction>::getLastColumn, "the most recently used column")
.def("replace_columns", &SparseMatrixBuilder<RationalFunction>::replaceColumns, R"dox(
Replaces all columns with id >= offset according to replacements.
Every state with id offset+i is replaced by the id in replacements[i]. Afterwards the columns are sorted.
:param std::vector<double> const& replacements: replacements Mapping indicating the replacements from offset+i -> value of i
:param int offset: Offset to add to each id in vector index.
)dox", py::arg("replacements"), py::arg("offset"))
;
// SparseMatrix
py::class_<SparseMatrix<double>>(m, "SparseMatrix", "Sparse matrix")
.def("__iter__", [](SparseMatrix<double>& matrix) {

112
src/storage/prism.cpp

@ -122,7 +122,7 @@ void define_prism(py::module& m) {
py::class_<BooleanVariable, std::shared_ptr<BooleanVariable>> boolean_variable(m, "PrismBooleanVariable", variable, "A program boolean variable in a Prism program");
boolean_variable.def("__str__", &streamToString<BooleanVariable>);
define_stateGeneration<uint32_t, storm::RationalNumber>(m);
//define_stateGeneration<uint32_t, storm::RationalNumber>(m);
}
class ValuationMapping {
@ -364,59 +364,59 @@ std::map<uint32_t, std::pair<storm::RationalNumber, storm::RationalNumber>> simu
template <typename StateType, typename ValueType>
void define_stateGeneration(py::module& m) {
py::class_<ValuationMapping, std::shared_ptr<ValuationMapping>> valuation_mapping(m, "ValuationMapping", "A valuation mapping for a state consists of a mapping from variable to value for each of the three types.");
valuation_mapping
.def_readonly("boolean_values", &ValuationMapping::booleanValues)
.def_readonly("integer_values", &ValuationMapping::integerValues)
.def_readonly("rational_values", &ValuationMapping::rationalValues)
.def("__str__", &ValuationMapping::toString);
py::class_<GeneratorChoice<StateType, ValueType>,
std::shared_ptr<GeneratorChoice<StateType, ValueType>>>
generator_choice(m, "GeneratorChoice", R"doc(
Representation of a choice taken by the generator.
:ivar origins: A list of command ids that generated this choice.
:vartype origins: List[int]
:ivar distribution: The probability distribution of this choice.
:vartype distribution: List[Pair[StateId, Probability]]
)doc");
generator_choice
.def_readonly("origins", &GeneratorChoice<StateType, ValueType>::origins)
.def_readonly("distribution", &GeneratorChoice<StateType, ValueType>::distribution);
py::class_<StateGenerator<StateType, ValueType>, std::shared_ptr<StateGenerator<StateType, ValueType>>> state_generator(m, "StateGenerator", R"doc(
Interactively explore states using Storm's PrismNextStateGenerator.
:ivar program: A PRISM program.
)doc");
state_generator
.def(py::init<storm::prism::Program const&>())
.def("load_initial_state", &StateGenerator<StateType, ValueType>::loadInitialState, R"doc(
Loads the (unique) initial state.
Multiple initial states are not supported.
:rtype: the ID of the initial state.
)doc")
.def("load", &StateGenerator<StateType, ValueType>::load, R"doc(
:param state_id: The ID of the state to load.
)doc")
.def("current_state_to_valuation", &StateGenerator<StateType, ValueType>::currentStateToValuation, R"doc(
Return a valuation for the currently loaded state.
:rtype: stormpy.ValuationMapping
)doc")
.def("current_state_satisfies", &StateGenerator<StateType, ValueType>::satisfies, R"doc(
Check if the currently loaded state satisfies the given expression.
:param stormpy.Expression expression: The expression to check against.
:rtype: bool
)doc")
.def("expand", &StateGenerator<StateType, ValueType>::expand, R"doc(
Expand the currently loaded state and return its successors.
:rtype: [GeneratorChoice]
)doc");
m.def("simulate", &simulate);
// py::class_<ValuationMapping, std::shared_ptr<ValuationMapping>> valuation_mapping(m, "ValuationMapping", "A valuation mapping for a state consists of a mapping from variable to value for each of the three types.");
// valuation_mapping
// .def_readonly("boolean_values", &ValuationMapping::booleanValues)
// .def_readonly("integer_values", &ValuationMapping::integerValues)
// .def_readonly("rational_values", &ValuationMapping::rationalValues)
// .def("__str__", &ValuationMapping::toString);
//
// py::class_<GeneratorChoice<StateType, ValueType>,
// std::shared_ptr<GeneratorChoice<StateType, ValueType>>>
// generator_choice(m, "GeneratorChoice", R"doc(
// Representation of a choice taken by the generator.
//
// :ivar origins: A list of command ids that generated this choice.
// :vartype origins: List[int]
// :ivar distribution: The probability distribution of this choice.
// :vartype distribution: List[Pair[StateId, Probability]]
// )doc");
// generator_choice
// .def_readonly("origins", &GeneratorChoice<StateType, ValueType>::origins)
// .def_readonly("distribution", &GeneratorChoice<StateType, ValueType>::distribution);
//
// py::class_<StateGenerator<StateType, ValueType>, std::shared_ptr<StateGenerator<StateType, ValueType>>> state_generator(m, "StateGenerator", R"doc(
// Interactively explore states using Storm's PrismNextStateGenerator.
//
// :ivar program: A PRISM program.
// )doc");
// state_generator
// .def(py::init<storm::prism::Program const&>())
// .def("load_initial_state", &StateGenerator<StateType, ValueType>::loadInitialState, R"doc(
// Loads the (unique) initial state.
// Multiple initial states are not supported.
//
// :rtype: the ID of the initial state.
// )doc")
// .def("load", &StateGenerator<StateType, ValueType>::load, R"doc(
// :param state_id: The ID of the state to load.
// )doc")
// .def("current_state_to_valuation", &StateGenerator<StateType, ValueType>::currentStateToValuation, R"doc(
// Return a valuation for the currently loaded state.
//
// :rtype: stormpy.ValuationMapping
// )doc")
// .def("current_state_satisfies", &StateGenerator<StateType, ValueType>::satisfies, R"doc(
// Check if the currently loaded state satisfies the given expression.
//
// :param stormpy.Expression expression: The expression to check against.
// :rtype: bool
// )doc")
// .def("expand", &StateGenerator<StateType, ValueType>::expand, R"doc(
// Expand the currently loaded state and return its successors.
//
// :rtype: [GeneratorChoice]
// )doc");
//
// m.def("simulate", &simulate);
}

11
tests/configurations.py

@ -4,7 +4,18 @@ import stormpy._config as config
# Skip not supported functionality
has_dft = config.storm_with_dft
has_gspn = config.storm_with_gspn
has_pars = config.storm_with_pars
# Check if numpy is available
try:
import numpy
has_numpy = True
except ImportError:
has_numpy = False
dft = pytest.mark.skipif(not has_dft, reason="No support for DFTs")
gspn = pytest.mark.skipif(not has_gspn, reason="No support for GSPNs")
pars = pytest.mark.skipif(not has_pars, reason="No support for parametric model checking")
numpy_avail = pytest.mark.skipif(not has_numpy, reason="Numpy not available")

2
tests/dft/test_io.py

@ -36,7 +36,7 @@ class TestDftLoad:
assert dft.nr_dynamic() == 1
assert not dft.can_have_nondeterminism()
@dft
class TestDftExport:
def test_export_dft_json_string(self):
dft = stormpy.dft.load_dft_galileo_file(get_example_path("dft", "hecs.dft"))

4
tests/gspn/conftest.py

@ -0,0 +1,4 @@
from configurations import has_gspn
if has_gspn:
import stormpy.gspn

252
tests/gspn/test_gspn.py

@ -0,0 +1,252 @@
import os
import stormpy
from configurations import gspn
@gspn
class TestGSPNBuilder:
def test_layout_info(self):
layout = stormpy.gspn.LayoutInfo()
assert layout.x == 0
assert layout.y == 0
assert layout.rotation == 0
layout.x = 1
assert layout.x == 1
layout_xy = stormpy.gspn.LayoutInfo(2, 3)
assert layout_xy.x == 2
assert layout_xy.rotation == 0
layout_xyr = stormpy.gspn.LayoutInfo(2, 3, 4)
assert layout_xyr.rotation == 4
def test_place(self):
p_id = 4
place = stormpy.gspn.Place(id=p_id)
assert p_id == place.get_id()
assert not place.has_restricted_capacity()
place.set_capacity(cap=5)
assert place.has_restricted_capacity()
assert place.get_capacity() == 5
p_name = "P_0"
place.set_name(name=p_name)
assert place.get_name() == p_name
p_tokens = 2
place.set_number_of_initial_tokens(p_tokens)
assert place.get_number_of_initial_tokens() == p_tokens
def test_transition(self):
# test TimedTransition
tt = stormpy.gspn.TimedTransition()
tt_name = " tt"
tt.set_name(tt_name)
assert tt_name == tt.get_name()
tt_rate = 0.2
tt.set_rate(tt_rate)
assert tt_rate == tt.get_rate()
# connect a place to this transition and test arcs
place = stormpy.gspn.Place(0)
# test input arcs
assert not tt.exists_input_arc(place)
tt.set_input_arc_multiplicity(place, 2)
assert tt.exists_input_arc(place)
assert tt.get_input_arc_multiplicity(place) == 2
tt.remove_input_arc(place)
assert not tt.exists_input_arc(place)
# test output arcs
assert not tt.exists_output_arc(place)
tt.set_output_arc_multiplicity(place, 3)
assert tt.exists_output_arc(place)
assert tt.get_output_arc_multiplicity(place) == 3
tt.remove_output_arc(place)
assert not tt.exists_output_arc(place)
# test inhibition arcs
assert not tt.exists_inhibition_arc(place)
tt.set_inhibition_arc_multiplicity(place, 5)
assert tt.exists_inhibition_arc(place)
assert tt.get_inhibition_arc_multiplicity(place) == 5
tt.remove_inhibition_arc(place)
assert not tt.exists_inhibition_arc(place)
# test ImmediateTransition
ti = stormpy.gspn.ImmediateTransition()
ti_name = " ti"
ti.set_name(ti_name)
assert ti_name == ti.get_name()
assert ti.no_weight_attached()
ti_weight = 0.2
ti.set_weight(ti_weight)
assert ti_weight == ti.get_weight()
assert not ti.no_weight_attached()
def test_build_gspn(self):
gspn_name = "gspn_test"
builder = stormpy.gspn.GSPNBuilder()
id_p_0 = builder.add_place()
id_p_1 = builder.add_place(initial_tokens=1)
id_p_2 = builder.add_place(initial_tokens=0, name="place_2")
id_p_3 = builder.add_place(capacity=2, initial_tokens=3, name="place_3")
p_layout = stormpy.gspn.LayoutInfo(1, 2)
builder.set_place_layout_info(id_p_0, p_layout)
id_ti_0 = builder.add_immediate_transition(priority=1, weight=0.5, name="ti_0")
id_ti_1 = builder.add_immediate_transition()
id_tt_0 = builder.add_timed_transition(priority=2, rate=0.4, name="tt_0")
id_tt_1 = builder.add_timed_transition(priority=0, rate=0.5, num_servers=2, name="tt_1")
t_layout = stormpy.gspn.LayoutInfo(1, 2)
builder.set_transition_layout_info(id_ti_0, t_layout)
# add arcs
builder.add_input_arc(id_p_0, id_ti_1, multiplicity=2)
builder.add_input_arc("place_2", "ti_0", multiplicity=2)
builder.add_output_arc(id_ti_1, id_p_2, multiplicity=2)
builder.add_output_arc("tt_0", "place_3", multiplicity=2)
builder.add_inhibition_arc(id_p_2, id_tt_0, multiplicity=2)
builder.add_inhibition_arc("place_3", "tt_0", multiplicity=2)
builder.add_normal_arc("place_3", "tt_0", multiplicity=2)
builder.add_normal_arc("tt_0", "place_3", multiplicity=2)
# test gspn composition
builder.set_name(gspn_name)
gspn = builder.build_gspn()
assert gspn.get_name() == gspn_name
assert gspn.is_valid()
assert gspn.get_number_of_immediate_transitions() == 2
assert gspn.get_number_of_timed_transitions() == 2
assert gspn.get_number_of_places() == 4
assert len(gspn.get_places()) == 4
assert len(gspn.get_immediate_transitions()) == 2
assert len(gspn.get_timed_transitions()) == 2
# test places
p_0 = gspn.get_place(id_p_0)
assert p_0.get_id() == id_p_0
p_1 = gspn.get_place(id_p_1)
assert p_1.get_id() == id_p_1
assert p_1.get_number_of_initial_tokens() == 1
p_2 = gspn.get_place(id_p_2)
assert p_2.get_id() == id_p_2
assert p_2.get_name() == "place_2"
p_3 = gspn.get_place(id_p_3)
assert p_3.get_name() == "place_3"
assert p_3.get_capacity() == 2
assert p_3.get_number_of_initial_tokens() == 3
assert p_3.has_restricted_capacity()
# test transitions
ti_0 = gspn.get_immediate_transition("ti_0")
assert ti_0.get_id() == id_ti_0
assert ti_0.get_weight() == 0.5
assert ti_0.get_priority() == 1
tt_0 = gspn.get_timed_transition("tt_0")
assert tt_0.get_id() == id_tt_0
assert tt_0.get_rate() == 0.4
assert tt_0.get_priority() == 2
tt_1 = gspn.get_timed_transition("tt_1")
assert tt_1.get_id() == id_tt_1
assert tt_1.get_number_of_servers() == 2
# test new name
gspn_new_name = "new_name"
gspn.set_name(gspn_new_name)
assert gspn.get_name() == gspn_new_name
def test_export_to_pnpro(self, tmpdir):
builder = stormpy.gspn.GSPNBuilder()
builder.set_name("gspn_test")
# add places and transitions
id_p_0 = builder.add_place()
id_p_1 = builder.add_place(initial_tokens=3, name="place_1", capacity=2)
id_ti_0 = builder.add_immediate_transition(priority=0, weight=0.5, name="ti_0")
id_tt_0 = builder.add_timed_transition(priority=0, rate=0.5, num_servers=2, name="tt_0")
gspn = builder.build_gspn()
export_file = os.path.join(str(tmpdir), "gspn.pnpro")
# export gspn to pnml
gspn.export_gspn_pnpro_file(export_file)
# import gspn
gspn_parser = stormpy.gspn.GSPNParser()
gspn_import = gspn_parser.parse(export_file)
# test imported gspn
assert gspn_import.get_name() == gspn.get_name()
assert gspn_import.get_number_of_timed_transitions() == gspn.get_number_of_timed_transitions()
assert gspn_import.get_number_of_immediate_transitions() == gspn.get_number_of_immediate_transitions()
assert gspn_import.get_number_of_places() == gspn.get_number_of_places()
p_0 = gspn_import.get_place(id_p_0)
assert p_0.get_id() == id_p_0
p_1 = gspn_import.get_place(id_p_1)
assert p_1.get_name() == "place_1"
# todo capacity info lost
# assert p_1.get_capacity() == 2
# assert p_1.has_restricted_capacity() == True
assert p_1.get_number_of_initial_tokens() == 3
ti_0 = gspn_import.get_immediate_transition("ti_0")
assert ti_0.get_id() == id_ti_0
tt_0 = gspn_import.get_timed_transition("tt_0")
assert tt_0.get_id() == id_tt_0
def test_export_to_pnml(self, tmpdir):
builder = stormpy.gspn.GSPNBuilder()
builder.set_name("gspn_test")
# add places and transitions
id_p_0 = builder.add_place()
id_p_1 = builder.add_place(initial_tokens=3, name="place_1", capacity=2)
id_ti_0 = builder.add_immediate_transition(priority=0, weight=0.5, name="ti_0")
id_tt_0 = builder.add_timed_transition(priority=0, rate=0.5, num_servers=2, name="tt_0")
gspn = builder.build_gspn()
export_file = os.path.join(str(tmpdir), "gspn.pnml")
# export gspn to pnml
gspn.export_gspn_pnml_file(export_file)
# import gspn
gspn_parser = stormpy.gspn.GSPNParser()
gspn_import = gspn_parser.parse(export_file)
# test imported gspn
assert gspn_import.get_name() == gspn.get_name()
assert gspn_import.get_number_of_timed_transitions() == gspn.get_number_of_timed_transitions()
assert gspn_import.get_number_of_immediate_transitions() == gspn.get_number_of_immediate_transitions()
assert gspn_import.get_number_of_places() == gspn.get_number_of_places()
p_0 = gspn_import.get_place(id_p_0)
assert p_0.get_id() == id_p_0
p_1 = gspn_import.get_place(id_p_1)
assert p_1.get_name() == "place_1"
assert p_1.get_capacity() == 2
assert p_1.get_number_of_initial_tokens() == 3
assert p_1.has_restricted_capacity()
ti_0 = gspn_import.get_immediate_transition("ti_0")
assert ti_0.get_id() == id_ti_0
tt_0 = gspn_import.get_timed_transition("tt_0")
assert tt_0.get_id() == id_tt_0

282
tests/storage/test_matrix_builder.py

@ -0,0 +1,282 @@
import stormpy
from configurations import numpy_avail
class TestMatrixBuilder:
def test_matrix_builder(self):
builder = stormpy.SparseMatrixBuilder(force_dimensions=True)
matrix = builder.build()
assert matrix.nr_columns == 0
assert matrix.nr_rows == 0
assert matrix.nr_entries == 0
builder_5x5 = stormpy.SparseMatrixBuilder(5, 5, force_dimensions=False)
builder_5x5.add_next_value(0, 0, 0)
builder_5x5.add_next_value(0, 1, 0.1)
builder_5x5.add_next_value(2, 2, 22)
builder_5x5.add_next_value(2, 3, 23)
assert builder_5x5.get_last_column() == 3
assert builder_5x5.get_last_row() == 2
builder_5x5.add_next_value(3, 2, 32)
builder_5x5.add_next_value(3, 4, 34)
builder_5x5.add_next_value(4, 3, 43)
matrix_5x5 = builder_5x5.build()
assert matrix_5x5.nr_columns == 5
assert matrix_5x5.nr_rows == 5
assert matrix_5x5.nr_entries == 7
for e in matrix_5x5:
assert (e.value() == 0.1 and e.column == 1) or e.value() == 0 or (e.value() > 20 and e.column > 1)
def test_parametric_matrix_builder(self):
builder = stormpy.ParametricSparseMatrixBuilder(force_dimensions=True)
matrix = builder.build()
assert matrix.nr_columns == 0
assert matrix.nr_rows == 0
assert matrix.nr_entries == 0
builder_5x5 = stormpy.ParametricSparseMatrixBuilder(5, 5, force_dimensions=False)
one_pol = stormpy.RationalRF(1)
one_pol = stormpy.FactorizedPolynomial(one_pol)
first_val = stormpy.FactorizedRationalFunction(one_pol)
two_pol = stormpy.RationalRF(2)
two_pol = stormpy.FactorizedPolynomial(two_pol)
sec_val = stormpy.FactorizedRationalFunction(two_pol)
builder_5x5.add_next_value(0, 0, first_val)
builder_5x5.add_next_value(0, 1, first_val)
builder_5x5.add_next_value(2, 2, sec_val)
builder_5x5.add_next_value(2, 3, sec_val)
assert builder_5x5.get_last_column() == 3
assert builder_5x5.get_last_row() == 2
builder_5x5.add_next_value(3, 2, sec_val)
builder_5x5.add_next_value(3, 4, sec_val)
builder_5x5.add_next_value(4, 3, sec_val)
matrix_5x5 = builder_5x5.build()
assert matrix_5x5.nr_columns == 5
assert matrix_5x5.nr_rows == 5
assert matrix_5x5.nr_entries == 7
for e in matrix_5x5:
assert (e.value() == first_val and e.column < 2) or (e.value() == sec_val and e.column > 1)
def test_matrix_replace_columns(self):
builder = stormpy.SparseMatrixBuilder(3, 4, force_dimensions=False)
builder.add_next_value(0, 0, 0)
builder.add_next_value(0, 1, 1)
builder.add_next_value(0, 2, 2)
builder.add_next_value(0, 3, 3)
builder.add_next_value(1, 1, 1)
builder.add_next_value(1, 2, 2)
builder.add_next_value(1, 3, 3)
builder.add_next_value(2, 1, 1)
builder.add_next_value(2, 2, 2)
builder.add_next_value(2, 3, 3)
# replace rows
builder.replace_columns([3, 2, 1], 1)
matrix = builder.build()
assert matrix.nr_entries == 10
# Check if columns where replaced
for e in matrix:
assert (e.value() == 0 and e.column == 0) or (e.value() == 3 and e.column == 1) or (
e.value() == 2 and e.column == 2) or (e.value() == 1 and e.column == 3)
def test_parametric_matrix_replace_columns(self):
builder = stormpy.ParametricSparseMatrixBuilder(3, 4, force_dimensions=False)
one_pol = stormpy.RationalRF(1)
one_pol = stormpy.FactorizedPolynomial(one_pol)
first_val = stormpy.FactorizedRationalFunction(one_pol, one_pol)
two_pol = stormpy.RationalRF(2)
two_pol = stormpy.FactorizedPolynomial(two_pol)
sec_val = stormpy.FactorizedRationalFunction(two_pol, two_pol)
third_val = stormpy.FactorizedRationalFunction(one_pol, two_pol)
builder.add_next_value(0, 1, first_val)
builder.add_next_value(0, 2, sec_val)
builder.add_next_value(0, 3, third_val)
builder.add_next_value(1, 1, first_val)
builder.add_next_value(1, 2, sec_val)
builder.add_next_value(1, 3, third_val)
builder.add_next_value(2, 1, first_val)
builder.add_next_value(2, 2, sec_val)
builder.add_next_value(2, 3, third_val)
# replace rows
builder.replace_columns([3, 2], 2)
matrix = builder.build()
assert matrix.nr_entries == 9
# Check if columns where replaced
for e in matrix:
assert (e.value() == first_val and e.column == 1) or (e.value() == third_val and e.column == 2) or (
e.value() == sec_val and e.column == 3)
def test_matrix_builder_row_grouping(self):
num_rows = 5
builder = stormpy.SparseMatrixBuilder(num_rows, 6, has_custom_row_grouping=True, row_groups=2)
builder.new_row_group(1)
assert builder.get_current_row_group_count() == 1
builder.new_row_group(4)
assert builder.get_current_row_group_count() == 2
matrix = builder.build()
assert matrix.get_row_group_start(0) == 1
assert matrix.get_row_group_end(0) == 4
assert matrix.get_row_group_start(1) == 4
assert matrix.get_row_group_end(1) == 5
def test_parametric_matrix_builder_row_grouping(self):
num_rows = 5
builder = stormpy.ParametricSparseMatrixBuilder(num_rows, 6, has_custom_row_grouping=True, row_groups=2)
builder.new_row_group(1)
assert builder.get_current_row_group_count() == 1
builder.new_row_group(4)
assert builder.get_current_row_group_count() == 2
matrix = builder.build()
assert matrix.get_row_group_start(0) == 1
assert matrix.get_row_group_end(0) == 4
assert matrix.get_row_group_start(1) == 4
assert matrix.get_row_group_end(1) == 5
@numpy_avail
def test_matrix_from_numpy(self):
import numpy as np
array = np.array([[0, 2],
[3, 4],
[0.1, 24],
[-0.3, -4]], dtype='float64')
matrix = stormpy.build_sparse_matrix(array)
# Check matrix dimension
assert matrix.nr_rows == array.shape[0]
assert matrix.nr_columns == array.shape[1]
assert matrix.nr_entries == 8
# Check matrix values
for r in range(array.shape[1]):
row = matrix.get_row(r)
for e in row:
assert (e.value() == array[r, e.column])
@numpy_avail
def test_parametric_matrix_from_numpy(self):
import numpy as np
one_pol = stormpy.RationalRF(1)
one_pol = stormpy.FactorizedPolynomial(one_pol)
first_val = stormpy.FactorizedRationalFunction(one_pol, one_pol)
two_pol = stormpy.RationalRF(2)
two_pol = stormpy.FactorizedPolynomial(two_pol)
sec_val = stormpy.FactorizedRationalFunction(two_pol, two_pol)
third_val = stormpy.FactorizedRationalFunction(one_pol, two_pol)
array = np.array([[sec_val, first_val],
[first_val, sec_val],
[sec_val, sec_val],
[third_val, third_val]])
matrix = stormpy.build_parametric_sparse_matrix(array)
# Check matrix dimension
assert matrix.nr_rows == array.shape[0]
assert matrix.nr_columns == array.shape[1]
assert matrix.nr_entries == 8
# Check matrix values
for r in range(array.shape[1]):
row = matrix.get_row(r)
for e in row:
assert (e.value() == array[r, e.column])
@numpy_avail
def test_matrix_from_numpy_row_grouping(self):
import numpy as np
array = np.array([[0, 2],
[3, 4],
[0.1, 24],
[-0.3, -4]], dtype='float64')
matrix = stormpy.build_sparse_matrix(array, row_group_indices=[1, 3])
# Check matrix dimension
assert matrix.nr_rows == array.shape[0]
assert matrix.nr_columns == array.shape[1]
assert matrix.nr_entries == 8
# Check matrix values
for r in range(array.shape[1]):
row = matrix.get_row(r)
for e in row:
assert (e.value() == array[r, e.column])
# Check row groups
assert matrix.get_row_group_start(0) == 1
assert matrix.get_row_group_end(0) == 3
assert matrix.get_row_group_start(1) == 3
assert matrix.get_row_group_end(1) == 4
@numpy_avail
def test_parametric_matrix_from_numpy_row_grouping(self):
import numpy as np
one_pol = stormpy.RationalRF(1)
one_pol = stormpy.FactorizedPolynomial(one_pol)
first_val = stormpy.FactorizedRationalFunction(one_pol, one_pol)
two_pol = stormpy.RationalRF(2)
two_pol = stormpy.FactorizedPolynomial(two_pol)
sec_val = stormpy.FactorizedRationalFunction(two_pol, two_pol)
third_val = stormpy.FactorizedRationalFunction(one_pol, two_pol)
array = np.array([[sec_val, first_val],
[first_val, sec_val],
[sec_val, sec_val],
[third_val, third_val]])
matrix = stormpy.build_parametric_sparse_matrix(array, row_group_indices=[1, 3])
# Check matrix dimension
assert matrix.nr_rows == array.shape[0]
assert matrix.nr_columns == array.shape[1]
assert matrix.nr_entries == 8
# Check matrix values
for r in range(array.shape[1]):
row = matrix.get_row(r)
for e in row:
assert (e.value() == array[r, e.column])
# Check row groups
assert matrix.get_row_group_start(0) == 1
assert matrix.get_row_group_end(0) == 3
assert matrix.get_row_group_start(1) == 3
assert matrix.get_row_group_end(1) == 4

2
tests/storage/test_state_generation.py

@ -1,5 +1,6 @@
import stormpy
from stormpy.examples.files import prism_dtmc_die
import pytest
class _DfsQueue:
def __init__(self):
@ -55,6 +56,7 @@ def _find_variable(program, name):
return var
return None
@pytest.mark.skipif(True, reason="State generation is broken")
def test_knuth_yao_die():
program, expression_parser = _load_program(prism_dtmc_die)
s_variable = _find_variable(program, "s")

1
travis/build-helper.sh

@ -65,7 +65,6 @@ run() {
if [[ "$TASK" == *Test* ]]
then
# Run tests
set +e
python setup.py test
fi

Loading…
Cancel
Save