diff --git a/CHANGELOG.md b/CHANGELOG.md
index 427bae8..c602295 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ Changelog
 Version 1.6.x
 -------------
 
+### Version 1.6.3 (to be released)
+- Support for exact arithmetic in models
+
+
 ### Version 1.6.2 (2020/09)
 Requires storm version >= 1.6.2 and pycarl version >= 2.0.4
 
diff --git a/lib/stormpy/__init__.py b/lib/stormpy/__init__.py
index d01e014..9aae889 100644
--- a/lib/stormpy/__init__.py
+++ b/lib/stormpy/__init__.py
@@ -297,9 +297,15 @@ def check_model_sparse(model, property, only_initial_states=False, extract_sched
         task.set_produce_schedulers(extract_scheduler)
         return core._parametric_model_checking_sparse_engine(model, task, environment=environment)
     else:
-        task = core.CheckTask(formula, only_initial_states)
-        task.set_produce_schedulers(extract_scheduler)
-        return core._model_checking_sparse_engine(model, task, environment=environment)
+
+        if model.is_exact:
+            task = core.ExactCheckTask(formula, only_initial_states)
+            task.set_produce_schedulers(extract_scheduler)
+            return core._exact_model_checking_sparse_engine(model, task, environment=environment)
+        else:
+            task = core.CheckTask(formula, only_initial_states)
+            task.set_produce_schedulers(extract_scheduler)
+            return core._model_checking_sparse_engine(model, task, environment=environment)
 
 
 def check_model_dd(model, property, only_initial_states=False, environment=Environment()):
diff --git a/lib/stormpy/simulator.py b/lib/stormpy/simulator.py
index 28e26e9..8c8c6ef 100644
--- a/lib/stormpy/simulator.py
+++ b/lib/stormpy/simulator.py
@@ -93,7 +93,10 @@ class SparseSimulator(Simulator):
     def __init__(self, model, seed=None):
         super().__init__(seed)
         self._model = model
-        self._engine = stormpy.core._DiscreteTimeSparseModelSimulatorDouble(model)
+        if self._model.is_exact:
+            self._engine = stormpy.core._DiscreteTimeSparseModelSimulatorExact(model)
+        else:
+            self._engine = stormpy.core._DiscreteTimeSparseModelSimulatorDouble(model)
         if seed is not None:
             self._engine.set_seed(seed)
         self._state_valuations = None
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e966019..dc01e13 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -101,24 +101,28 @@ void define_build(py::module& m) {
 
     // Build model
     m.def("_build_sparse_model_from_symbolic_description", &buildSparseModel<double>, "Build the model in sparse representation", py::arg("model_description"), py::arg("formulas") = std::vector<std::shared_ptr<storm::logic::Formula const>>(), py::arg("use_jit") = false, py::arg("doctor") = false);
+    m.def("_build_sparse_exact_model_from_symbolic_description", &buildSparseModel<storm::RationalNumber>, "Build the model in sparse representation with exact number representation", py::arg("model_description"), py::arg("formulas") = std::vector<std::shared_ptr<storm::logic::Formula const>>(), py::arg("use_jit") = false, py::arg("doctor") = false);
     m.def("_build_sparse_parametric_model_from_symbolic_description", &buildSparseModel<storm::RationalFunction>, "Build the parametric model in sparse representation", py::arg("model_description"), py::arg("formulas") = std::vector<std::shared_ptr<storm::logic::Formula const>>(), py::arg("use_jit") = false, py::arg("doctor") = false);
     m.def("build_sparse_model_with_options", &buildSparseModelWithOptions<double>, "Build the model in sparse representation", py::arg("model_description"), py::arg("options"), py::arg("use_jit") = false, py::arg("doctor") = false);
+    m.def("build_sparse_exact_model_with_options", &buildSparseModelWithOptions<storm::RationalNumber>, "Build the model in sparse representation with exact number representation", py::arg("model_description"), py::arg("options"), py::arg("use_jit") = false, py::arg("doctor") = false);
     m.def("build_sparse_parametric_model_with_options", &buildSparseModelWithOptions<storm::RationalFunction>, "Build the model in sparse representation", py::arg("model_description"), py::arg("options"), py::arg("use_jit") = false, py::arg("doctor") = false);
     m.def("_build_symbolic_model_from_symbolic_description", &buildSymbolicModel<storm::dd::DdType::Sylvan, double>, "Build the model in symbolic representation", py::arg("model_description"), py::arg("formulas") = std::vector<std::shared_ptr<storm::logic::Formula const>>());
     m.def("_build_symbolic_parametric_model_from_symbolic_description", &buildSymbolicModel<storm::dd::DdType::Sylvan, storm::RationalFunction>, "Build the parametric model in symbolic representation", py::arg("model_description"), py::arg("formulas") = std::vector<std::shared_ptr<storm::logic::Formula const>>());
     m.def("_build_sparse_model_from_drn", &storm::api::buildExplicitDRNModel<double>, "Build the model from DRN", py::arg("file"), py::arg("options") = storm::parser::DirectEncodingParserOptions());
+    m.def("_build_sparse_exact_model_from_drn", &storm::api::buildExplicitDRNModel<storm::RationalNumber>, "Build the model from DRN", py::arg("file"), py::arg("options") = storm::parser::DirectEncodingParserOptions());
     m.def("_build_sparse_parametric_model_from_drn", &storm::api::buildExplicitDRNModel<storm::RationalFunction>, "Build the parametric model from DRN", py::arg("file"), py::arg("options") = storm::parser::DirectEncodingParserOptions());
     m.def("build_sparse_model_from_explicit", &storm::api::buildExplicitModel<double>, "Build the model model from explicit input", py::arg("transition_file"), py::arg("labeling_file"), py::arg("state_reward_file") = "", py::arg("transition_reward_file") = "", py::arg("choice_labeling_file") = "");
 
     m.def("make_sparse_model_builder", &storm::api::makeExplicitModelBuilder<double>, "Construct a builder instance", py::arg("model_description"), py::arg("options"));
+    m.def("make_sparse_model_builder_exact", &storm::api::makeExplicitModelBuilder<storm::RationalNumber>, "Construct a builder instance", py::arg("model_description"), py::arg("options"));
     m.def("make_sparse_model_builder_parametric", &storm::api::makeExplicitModelBuilder<double>, "Construct a builder instance", py::arg("model_description"), py::arg("options"));
 
-    py::class_<storm::builder::ExplicitModelBuilder<double>>(m, "ExplicitModelBuilder_Double", "Model builder for sparse models")
+    py::class_<storm::builder::ExplicitModelBuilder<double>>(m, "ExplicitModelBuilder", "Model builder for sparse models")
         .def("build", &storm::builder::ExplicitModelBuilder<double>::build, "Build the model")
         .def("export_lookup", &storm::builder::ExplicitModelBuilder<double>::exportExplicitStateLookup, "Export a lookup model")
     ;
 
-    py::class_<storm::builder::ExplicitModelBuilder<storm::RationalFunction>>(m, "ExplicitModelBuilder_RF", "Model builder for sparse models")
+    py::class_<storm::builder::ExplicitModelBuilder<storm::RationalFunction>>(m, "ExplicitParametricModelBuilder", "Model builder for sparse models")
         .def("build", &storm::builder::ExplicitModelBuilder<storm::RationalFunction>::build, "Build the model")
         .def("export_lookup", &storm::builder::ExplicitModelBuilder<storm::RationalFunction>::exportExplicitStateLookup, "Export a lookup model")
     ;
@@ -164,6 +168,8 @@ void define_export(py::module& m) {
     opts.def(py::init<>());
     opts.def_readwrite("allow_placeholders", &storm::exporter::DirectEncodingOptions::allowPlaceholders);
     // Export
+    // TODO make one export_to_drn that infers which of the folliwng to use.
     m.def("export_to_drn", &exportDRN<double>, "Export model in DRN format", py::arg("model"), py::arg("file"), py::arg("options")=storm::exporter::DirectEncodingOptions());
+    m.def("export_exact_to_drn", &exportDRN<double>, "Export model in DRN format", py::arg("model"), py::arg("file"), py::arg("options")=storm::exporter::DirectEncodingOptions());
     m.def("export_parametric_to_drn", &exportDRN<storm::RationalFunction>, "Export parametric model in DRN format", py::arg("model"), py::arg("file"), py::arg("options")=storm::exporter::DirectEncodingOptions());
 }
diff --git a/src/core/modelchecking.cpp b/src/core/modelchecking.cpp
index f0c24d1..7175568 100644
--- a/src/core/modelchecking.cpp
+++ b/src/core/modelchecking.cpp
@@ -66,6 +66,12 @@ void define_modelchecking(py::module& m) {
         .def(py::init<storm::logic::Formula const&, bool>(), py::arg("formula"), py::arg("only_initial_states") = false)
         .def("set_produce_schedulers", &CheckTask<double>::setProduceSchedulers, "Set whether schedulers should be produced (if possible)", py::arg("produce_schedulers") = true)
     ;
+    // CheckTask
+    py::class_<CheckTask<storm::RationalNumber>, std::shared_ptr<CheckTask<storm::RationalNumber>>>(m, "ExactCheckTask", "Task for model checking with exact numbers")
+            //m.def("create_check_task", &storm::api::createTask, "Create task for verification", py::arg("formula"), py::arg("only_initial_states") = false);
+            .def(py::init<storm::logic::Formula const&, bool>(), py::arg("formula"), py::arg("only_initial_states") = false)
+            .def("set_produce_schedulers", &CheckTask<storm::RationalNumber>::setProduceSchedulers, "Set whether schedulers should be produced (if possible)", py::arg("produce_schedulers") = true)
+            ;
     py::class_<CheckTask<storm::RationalFunction>, std::shared_ptr<CheckTask<storm::RationalFunction>>>(m, "ParametricCheckTask", "Task for parametric model checking")
     //m.def("create_check_task", &storm::api::createTask, "Create task for verification", py::arg("formula"), py::arg("only_initial_states") = false);
         .def(py::init<storm::logic::Formula const&, bool>(), py::arg("formula"), py::arg("only_initial_states") = false)
@@ -73,8 +79,10 @@ void define_modelchecking(py::module& m) {
     ;
 
     // Model checking
-    m.def("model_checking_fully_observable", &modelCheckingFullyObservableSparseEngine<double>, py::arg("model"), py::arg("task"), py::arg("environment")  = storm::Environment());
+    m.def("_model_checking_fully_observable", &modelCheckingFullyObservableSparseEngine<double>, py::arg("model"), py::arg("task"), py::arg("environment")  = storm::Environment());
+    m.def("_exact_model_checking_fully_observable", &modelCheckingFullyObservableSparseEngine<storm::RationalNumber>, py::arg("model"), py::arg("task"), py::arg("environment")  = storm::Environment());
     m.def("_model_checking_sparse_engine", &modelCheckingSparseEngine<double>, "Perform model checking using the sparse engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment());
+    m.def("_exact_model_checking_sparse_engine",  &modelCheckingSparseEngine<storm::RationalNumber>, "Perform model checking using the sparse engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment());
     m.def("_parametric_model_checking_sparse_engine", &modelCheckingSparseEngine<storm::RationalFunction>, "Perform parametric model checking using the sparse engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment());
     m.def("_model_checking_dd_engine", &modelCheckingDdEngine<storm::dd::DdType::Sylvan, double>, "Perform model checking using the dd engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment());
     m.def("_parametric_model_checking_dd_engine", &modelCheckingDdEngine<storm::dd::DdType::Sylvan, storm::RationalFunction>, "Perform parametric model checking using the dd engine", py::arg("model"), py::arg("task"), py::arg("environment") = storm::Environment());
diff --git a/src/core/simulator.cpp b/src/core/simulator.cpp
index 098dd1e..42a0731 100644
--- a/src/core/simulator.cpp
+++ b/src/core/simulator.cpp
@@ -1,14 +1,17 @@
 #include "simulator.h"
 #include <storm/simulator/DiscreteTimeSparseModelSimulator.h>
 
-void define_sparse_model_simulator(py::module& m) {
-    py::class_<storm::simulator::DiscreteTimeSparseModelSimulator<double>> dtsmsd(m, "_DiscreteTimeSparseModelSimulatorDouble", "Simulator for sparse discrete-time models in memory (ValueType = double)");
-    dtsmsd.def(py::init<storm::models::sparse::Model<double> const&>());
-    dtsmsd.def("set_seed", &storm::simulator::DiscreteTimeSparseModelSimulator<double>::setSeed, py::arg("seed"));
-    dtsmsd.def("step", &storm::simulator::DiscreteTimeSparseModelSimulator<double>::step, py::arg("action"));
-    dtsmsd.def("random_step", &storm::simulator::DiscreteTimeSparseModelSimulator<double>::randomStep);
-    dtsmsd.def("get_last_reward", &storm::simulator::DiscreteTimeSparseModelSimulator<double>::getLastRewards);
-    dtsmsd.def("get_current_state", &storm::simulator::DiscreteTimeSparseModelSimulator<double>::getCurrentState);
-    dtsmsd.def("reset_to_initial_state", &storm::simulator::DiscreteTimeSparseModelSimulator<double>::resetToInitial);
+template<typename ValueType>
+void define_sparse_model_simulator(py::module& m, std::string const& vtSuffix) {
+    py::class_<storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>> dtsmsd(m, ("_DiscreteTimeSparseModelSimulator" + vtSuffix).c_str(), "Simulator for sparse discrete-time models in memory (ValueType = double)");
+    dtsmsd.def(py::init<storm::models::sparse::Model<ValueType> const&>());
+    dtsmsd.def("set_seed", &storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>::setSeed, py::arg("seed"));
+    dtsmsd.def("step", &storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>::step, py::arg("action"));
+    dtsmsd.def("random_step", &storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>::randomStep);
+    dtsmsd.def("get_last_reward", &storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>::getLastRewards);
+    dtsmsd.def("get_current_state", &storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>::getCurrentState);
+    dtsmsd.def("reset_to_initial_state", &storm::simulator::DiscreteTimeSparseModelSimulator<ValueType>::resetToInitial);
+}
 
-}
\ No newline at end of file
+template void define_sparse_model_simulator<double>(py::module& m, std::string const& vtSuffix);
+template void define_sparse_model_simulator<storm::RationalNumber>(py::module& m, std::string const& vtSuffix);
diff --git a/src/core/simulator.h b/src/core/simulator.h
index 4edb35b..bcb22e6 100644
--- a/src/core/simulator.h
+++ b/src/core/simulator.h
@@ -2,4 +2,5 @@
 
 #include "common.h"
 
-void define_sparse_model_simulator(py::module& m);
\ No newline at end of file
+template<typename ValueType>
+void define_sparse_model_simulator(py::module& m, std::string const& vtSuffix);
\ No newline at end of file
diff --git a/src/mod_core.cpp b/src/mod_core.cpp
index f16db4d..18c8680 100644
--- a/src/mod_core.cpp
+++ b/src/mod_core.cpp
@@ -34,5 +34,7 @@ PYBIND11_MODULE(core, m) {
     define_input(m);
     define_graph_constraints(m);
     define_transformation(m);
-    define_sparse_model_simulator(m);
+    define_sparse_model_simulator<double>(m, "Double");
+    define_sparse_model_simulator<storm::RationalNumber>(m, "Exact");
+
 }
diff --git a/src/mod_pomdp.cpp b/src/mod_pomdp.cpp
index 4e86642..9cb8766 100644
--- a/src/mod_pomdp.cpp
+++ b/src/mod_pomdp.cpp
@@ -14,11 +14,14 @@ PYBIND11_MODULE(pomdp, m) {
     py::options options;
     options.disable_function_signatures();
 #endif
-    define_tracker(m);
+    define_tracker<double>(m, "Double");
+    define_tracker<storm::RationalNumber>(m, "Exact");
     define_qualitative_policy_search<double>(m, "Double");
     define_qualitative_policy_search_nt(m);
     define_memory(m);
     define_transformations_nt(m);
     define_transformations<double>(m, "Double");
+    define_transformations<storm::RationalNumber>(m, "Exact");
+
     define_transformations<storm::RationalFunction>(m, "Rf");
 }
diff --git a/src/mod_storage.cpp b/src/mod_storage.cpp
index ba53955..09e5b6f 100644
--- a/src/mod_storage.cpp
+++ b/src/mod_storage.cpp
@@ -28,12 +28,19 @@ PYBIND11_MODULE(storage, m) {
     define_bitvector(m);
     define_dd<storm::dd::DdType::Sylvan>(m, "Sylvan");
     define_dd_nt(m);
+
     define_model(m);
+    define_sparse_model<double>(m, "");
+    define_sparse_model<storm::RationalNumber>(m, "Exact");
+    define_sparse_parametric_model(m);
     define_statevaluation(m);
-    define_sparse_model(m);
-    define_sparse_matrix(m);
+    define_sparse_matrix<double>(m, "");
+    define_sparse_matrix<storm::RationalNumber>(m, "Exact");
+    define_sparse_matrix<storm::RationalFunction>(m, "Parametric");
+    define_sparse_matrix_nt(m);
     define_symbolic_model<storm::dd::DdType::Sylvan>(m, "Sylvan");
-    define_state(m);
+    define_state<double>(m, "");
+    define_state<storm::RationalNumber>(m, "Exact");
     define_prism(m);
     define_jani(m);
     define_jani_transformers(m);
@@ -41,6 +48,9 @@ PYBIND11_MODULE(storage, m) {
     define_origins(m);
     define_expressions(m);
     define_scheduler<double>(m, "Double");
+    define_scheduler<storm::RationalNumber>(m, "Exact");
     define_distribution<double>(m, "Double");
-    define_sparse_model_components(m);
+    define_sparse_model_components<double>(m, "");
+    define_sparse_model_components<storm::RationalNumber>(m, "Exact");
+
 }
diff --git a/src/pomdp/tracker.cpp b/src/pomdp/tracker.cpp
index 6798be4..468d9aa 100644
--- a/src/pomdp/tracker.cpp
+++ b/src/pomdp/tracker.cpp
@@ -7,39 +7,39 @@
 template<typename ValueType> using SparsePomdp = storm::models::sparse::Pomdp<ValueType>;
 template<typename ValueType> using SparsePomdpTracker = storm::generator::BeliefSupportTracker<ValueType>;
 
-template<typename ValueType> using NDPomdpTrackerSparse = storm::generator::NondeterministicBeliefTracker<double, storm::generator::SparseBeliefState<double>>;
-template<typename ValueType> using NDPomdpTrackerDense = storm::generator::NondeterministicBeliefTracker<double, storm::generator::ObservationDenseBeliefState<double>>;
-
-
-void define_tracker(py::module& m) {
-    py::class_<storm::generator::BeliefSupportTracker<double>> tracker(m, "BeliefSupportTrackerDouble", "Tracker for BeliefSupports");
-    tracker.def(py::init<SparsePomdp<double> const&>(), py::arg("pomdp"));
-    tracker.def("get_current_belief_support", &SparsePomdpTracker<double>::getCurrentBeliefSupport, "What is the support given the trace so far");
-    tracker.def("track", &SparsePomdpTracker<double>::track, py::arg("action"), py::arg("observation"));
-
-    py::class_<storm::generator::SparseBeliefState<double>> sbel(m, "SparseBeliefStateDouble", "Belief state in sparse format");
-    sbel.def("get", &storm::generator::SparseBeliefState<double>::get, py::arg("state"));
-    sbel.def_property_readonly("risk", &storm::generator::SparseBeliefState<double>::getRisk);
-    sbel.def("__str__", &storm::generator::SparseBeliefState<double>::toString);
-    sbel.def_property_readonly("is_valid", &storm::generator::SparseBeliefState<double>::isValid);
+template<typename ValueType> using NDPomdpTrackerSparse = storm::generator::NondeterministicBeliefTracker<ValueType, storm::generator::SparseBeliefState<ValueType>>;
+template<typename ValueType> using NDPomdpTrackerDense = storm::generator::NondeterministicBeliefTracker<ValueType, storm::generator::ObservationDenseBeliefState<ValueType>>;
+
+
+template<typename ValueType>
+void define_tracker(py::module& m, std::string const& vtSuffix) {
+    py::class_<storm::generator::BeliefSupportTracker<ValueType>> tracker(m, ("BeliefSupportTracker" + vtSuffix).c_str(), "Tracker for BeliefSupports");
+    tracker.def(py::init<SparsePomdp<ValueType> const&>(), py::arg("pomdp"));
+    tracker.def("get_current_belief_support", &SparsePomdpTracker<ValueType>::getCurrentBeliefSupport, "What is the support given the trace so far");
+    tracker.def("track", &SparsePomdpTracker<ValueType>::track, py::arg("action"), py::arg("observation"));
+
+    py::class_<storm::generator::SparseBeliefState<ValueType>> sbel(m, ("SparseBeliefState" + vtSuffix).c_str(), "Belief state in sparse format");
+    sbel.def("get", &storm::generator::SparseBeliefState<ValueType>::get, py::arg("state"));
+    sbel.def_property_readonly("risk", &storm::generator::SparseBeliefState<ValueType>::getRisk);
+    sbel.def("__str__", &storm::generator::SparseBeliefState<ValueType>::toString);
+    sbel.def_property_readonly("is_valid", &storm::generator::SparseBeliefState<ValueType>::isValid);
 //
 //    py::class_<storm::generator::ObservationDenseBeliefState<double>> dbel(m, "DenseBeliefStateDouble", "Belief state in dense format");
 //    dbel.def("get", &storm::generator::ObservationDenseBeliefState<double>::get, py::arg("state"));
 //    dbel.def_property_readonly("risk", &storm::generator::ObservationDenseBeliefState<double>::getRisk);
 //    dbel.def("__str__", &storm::generator::ObservationDenseBeliefState<double>::toString);
 
-    py::class_<NDPomdpTrackerSparse<double>> ndetbelieftracker(m, "NondeterministicBeliefTrackerDoubleSparse", "Tracker for belief states and uncontrollable actions");
-    ndetbelieftracker.def(py::init<SparsePomdp<double> const&>(), py::arg("pomdp"));
-    ndetbelieftracker.def("reset", &NDPomdpTrackerSparse<double>::reset);
-    ndetbelieftracker.def("set_risk", &NDPomdpTrackerSparse<double>::setRisk, py::arg("risk"));
-    ndetbelieftracker.def("obtain_current_risk",&NDPomdpTrackerSparse<double>::getCurrentRisk, py::arg("max")=true);
-    ndetbelieftracker.def("track", &NDPomdpTrackerSparse<double>::track, py::arg("observation"));
-    ndetbelieftracker.def("obtain_beliefs", &NDPomdpTrackerSparse<double>::getCurrentBeliefs);
-    ndetbelieftracker.def("size", &NDPomdpTrackerSparse<double>::getNumberOfBeliefs);
-    ndetbelieftracker.def("dimension", &NDPomdpTrackerSparse<double>::getCurrentDimension);
-    ndetbelieftracker.def("obtain_last_observation", &NDPomdpTrackerSparse<double>::getCurrentObservation);
-    ndetbelieftracker.def("reduce",&NDPomdpTrackerSparse<double>::reduce);
-
+    py::class_<NDPomdpTrackerSparse<ValueType>> ndetbelieftracker(m, ("NondeterministicBeliefTracker" + vtSuffix + "Sparse").c_str(), "Tracker for belief states and uncontrollable actions");
+    ndetbelieftracker.def(py::init<SparsePomdp<ValueType> const&>(), py::arg("pomdp"));
+    ndetbelieftracker.def("reset", &NDPomdpTrackerSparse<ValueType>::reset);
+    ndetbelieftracker.def("set_risk", &NDPomdpTrackerSparse<ValueType>::setRisk, py::arg("risk"));
+    ndetbelieftracker.def("obtain_current_risk",&NDPomdpTrackerSparse<ValueType>::getCurrentRisk, py::arg("max")=true);
+    ndetbelieftracker.def("track", &NDPomdpTrackerSparse<ValueType>::track, py::arg("observation"));
+    ndetbelieftracker.def("obtain_beliefs", &NDPomdpTrackerSparse<ValueType>::getCurrentBeliefs);
+    ndetbelieftracker.def("size", &NDPomdpTrackerSparse<ValueType>::getNumberOfBeliefs);
+    ndetbelieftracker.def("dimension", &NDPomdpTrackerSparse<ValueType>::getCurrentDimension);
+    ndetbelieftracker.def("obtain_last_observation", &NDPomdpTrackerSparse<ValueType>::getCurrentObservation);
+    ndetbelieftracker.def("reduce",&NDPomdpTrackerSparse<ValueType>::reduce);
 
 //    py::class_<NDPomdpTrackerDense<double>> ndetbelieftrackerd(m, "NondeterministicBeliefTrackerDoubleDense", "Tracker for belief states and uncontrollable actions");
 //    ndetbelieftrackerd.def(py::init<SparsePomdp<double> const&>(), py::arg("pomdp"));
@@ -51,4 +51,7 @@ void define_tracker(py::module& m) {
 //    ndetbelieftrackerd.def("obtain_last_observation", &NDPomdpTrackerDense<double>::getCurrentObservation);
 //    ndetbelieftrackerd.def("reduce",&NDPomdpTrackerDense<double>::reduce);
 
-}
\ No newline at end of file
+}
+
+template void define_tracker<double>(py::module& m, std::string const& vtSuffix);
+template void define_tracker<storm::RationalNumber>(py::module& m, std::string const& vtSuffix);
diff --git a/src/pomdp/tracker.h b/src/pomdp/tracker.h
index 9370d29..b4b91ad 100644
--- a/src/pomdp/tracker.h
+++ b/src/pomdp/tracker.h
@@ -1,4 +1,5 @@
 #pragma once
 #include "common.h"
 
-void define_tracker(py::module& m);
+template<typename ValueType>
+void define_tracker(py::module& m, std::string const& vtSuffix);
diff --git a/src/pomdp/transformations.cpp b/src/pomdp/transformations.cpp
index 91131a5..45c1e7d 100644
--- a/src/pomdp/transformations.cpp
+++ b/src/pomdp/transformations.cpp
@@ -46,13 +46,6 @@ void define_transformations_nt(py::module &m) {
             .value("full", storm::transformer::PomdpFscApplicationMode::FULL)
             ;
 
-
-    py::class_<storm::pomdp::ObservationTraceUnfolder<double>> unfolder(m, "ObservationTraceUnfolderDouble", "Unfolds observation traces in models");
-    unfolder.def(py::init<storm::models::sparse::Pomdp<double> const&,  std::vector<double> const&, std::shared_ptr<storm::expressions::ExpressionManager>&>(), py::arg("model"), py::arg("risk"), py::arg("expression_manager"));
-    unfolder.def("transform", &storm::pomdp::ObservationTraceUnfolder<double>::transform, py::arg("trace"));
-    unfolder.def("extend", &storm::pomdp::ObservationTraceUnfolder<double>::extend, py::arg("new_observation"));
-    unfolder.def("reset", &storm::pomdp::ObservationTraceUnfolder<double>::reset, py::arg("new_observation"));
-
 }
 
 template<typename ValueType>
@@ -63,8 +56,13 @@ void define_transformations(py::module& m, std::string const& vtSuffix) {
     m.def(("_apply_unknown_fsc_" + vtSuffix).c_str(), &apply_unknown_fsc<ValueType>, "Apply unknown FSC",py::arg("pomdp"), py::arg("application_mode")=storm::transformer::PomdpFscApplicationMode::SIMPLE_LINEAR);
     //m.def(("_unfold_trace_" + vtSuffix).c_str(), &unfold_trace<ValueType>, "Unfold observed trace", py::arg("pomdp"), py::arg("expression_manager"),py::arg("observation_trace"), py::arg("risk_definition"));
 
-
+    py::class_<storm::pomdp::ObservationTraceUnfolder<ValueType>> unfolder(m, ("ObservationTraceUnfolder" + vtSuffix).c_str(), "Unfolds observation traces in models");
+    unfolder.def(py::init<storm::models::sparse::Pomdp<ValueType> const&,  std::vector<ValueType> const&, std::shared_ptr<storm::expressions::ExpressionManager>&>(), py::arg("model"), py::arg("risk"), py::arg("expression_manager"));
+    unfolder.def("transform", &storm::pomdp::ObservationTraceUnfolder<ValueType>::transform, py::arg("trace"));
+    unfolder.def("extend", &storm::pomdp::ObservationTraceUnfolder<ValueType>::extend, py::arg("new_observation"));
+    unfolder.def("reset", &storm::pomdp::ObservationTraceUnfolder<ValueType>::reset, py::arg("new_observation"));
 }
 
 template void define_transformations<double>(py::module& m, std::string const& vtSuffix);
+template void define_transformations<storm::RationalNumber>(py::module& m, std::string const& vtSuffix);
 template void define_transformations<storm::RationalFunction>(py::module& m, std::string const& vtSuffix);
\ No newline at end of file
diff --git a/src/storage/distribution.cpp b/src/storage/distribution.cpp
index 79a03a2..d2ab67b 100644
--- a/src/storage/distribution.cpp
+++ b/src/storage/distribution.cpp
@@ -1,3 +1,4 @@
+#include <storm/adapters/RationalNumberAdapter.h>
 #include "distribution.h"
 #include "src/helpers.h"
 
@@ -14,4 +15,5 @@ void define_distribution(py::module& m, std::string vt_suffix) {
 }
 
 
-template void define_distribution<double>(py::module&, std::string vt_suffix);
\ No newline at end of file
+template void define_distribution<double>(py::module&, std::string vt_suffix);
+template void define_distribution<storm::RationalNumber>(py::module&, std::string vt_suffix);
\ No newline at end of file
diff --git a/src/storage/jani.cpp b/src/storage/jani.cpp
index 3dd5831..2f8a88f 100644
--- a/src/storage/jani.cpp
+++ b/src/storage/jani.cpp
@@ -140,6 +140,7 @@ void define_jani(py::module& m) {
     py::class_<Variable, std::shared_ptr<Variable>> variable(m, "JaniVariable", "A Variable in JANI");
     variable.def_property_readonly("name", &Variable::getName, "name of constant")
             .def_property_readonly("expression_variable", &Variable::getExpressionVariable, "expression variable for this variable")
+            //.def_property_readonly("initial_value_expression", &Variable::getInitialValue)
             ;
 
     py::class_<BoundedIntegerVariable, std::shared_ptr<BoundedIntegerVariable>> bivariable(m, "JaniBoundedIntegerVariable", "A Bounded Integer", variable);
diff --git a/src/storage/matrix.cpp b/src/storage/matrix.cpp
index bbbc859..c44bec0 100644
--- a/src/storage/matrix.cpp
+++ b/src/storage/matrix.cpp
@@ -12,30 +12,29 @@ template<typename ValueType> using MatrixEntry = storm::storage::MatrixEntry<ent
 using RationalFunction = storm::RationalFunction;
 using row_index = unsigned int;
 
-void define_sparse_matrix(py::module& m) {
+void define_sparse_matrix_nt(py::module& m) {
+    m.def("_topological_sort_double", [](SparseMatrix<double>& matrix, std::vector<uint64_t> initial) { return storm::utility::graph::getTopologicalSort(matrix, initial); }, "matrix"_a, "initial"_a,  "get topological sort w.r.t. a transition matrix");
+    m.def("_topological_sort_rf", [](SparseMatrix<storm::RationalFunction>& matrix, std::vector<uint64_t> initial) { return storm::utility::graph::getTopologicalSort(matrix, initial); }, "matrix"_a, "initial"_a,  "get topological sort w.r.t. a transition matrix");
+}
+
+template<typename ValueType>
+void define_sparse_matrix(py::module& m, std::string const& vtSuffix) {
 
     // MatrixEntry
-    py::class_<MatrixEntry<double>>(m, "SparseMatrixEntry", "Entry of sparse matrix")
-        .def("__str__", &streamToString<MatrixEntry<double>>)
+    py::class_<MatrixEntry<ValueType>>(m, (vtSuffix + "SparseMatrixEntry").c_str(), "Entry of sparse matrix")
+        .def("__str__", &streamToString<MatrixEntry<ValueType>>)
         //def_property threw "pointer being freed not allocated" after exiting
-        .def("value", &MatrixEntry<double>::getValue, "Value")
-        .def("set_value", &MatrixEntry<double>::setValue, py::arg("value"), "Set value")
-        .def_property_readonly("column", &MatrixEntry<double>::getColumn, "Column")
+        .def("value", &MatrixEntry<ValueType>::getValue, "Value")
+        .def("set_value", &MatrixEntry<ValueType>::setValue, py::arg("value"), "Set value")
+        .def_property_readonly("column", &MatrixEntry<ValueType>::getColumn, "Column")
     ;
 
-    py::class_<MatrixEntry<RationalFunction>>(m, "ParametricSparseMatrixEntry", "Entry of parametric sparse matrix")
-        .def("__str__", &streamToString<MatrixEntry<RationalFunction>>)
-        //def_property threw "pointer being freed not allocated" after exiting
-        .def("value", &MatrixEntry<RationalFunction>::getValue, "Value")
-        .def("set_value", &MatrixEntry<RationalFunction>::setValue, py::arg("value"), "Set value")
-        .def_property_readonly("column", &MatrixEntry<RationalFunction>::getColumn, "Column")
-    ;
 
     // SparseMatrixBuilder
-    py::class_<SparseMatrixBuilder<double>>(m, "SparseMatrixBuilder", "Builder of sparse matrix")
+    py::class_<SparseMatrixBuilder<ValueType>>(m, ( vtSuffix + "SparseMatrixBuilder").c_str(), "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(
+            .def("add_next_value", &SparseMatrixBuilder<ValueType>::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.
@@ -50,45 +49,12 @@ void define_sparse_matrix(py::module& m) {
               :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(
+            .def("new_row_group", &SparseMatrixBuilder<ValueType>::newRowGroup, py::arg("starting_row"), "Start a new row group in the matrix")
+            .def("build", &SparseMatrixBuilder<ValueType>::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<ValueType>::getLastRow, "Get the most recently used row")
+            .def("get_current_row_group_count", &SparseMatrixBuilder<ValueType>::getCurrentRowGroupCount, "Get the current row group count")
+            .def("get_last_column", &SparseMatrixBuilder<ValueType>::getLastColumn, "the most recently used column")
+            .def("replace_columns", &SparseMatrixBuilder<ValueType>::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.
@@ -99,26 +65,26 @@ void define_sparse_matrix(py::module& m) {
     ;
 
     // SparseMatrix
-    py::class_<SparseMatrix<double>>(m, "SparseMatrix", "Sparse matrix")
-        .def("__iter__", [](SparseMatrix<double>& matrix) {
+    py::class_<SparseMatrix<ValueType>>(m, (vtSuffix + "SparseMatrix").c_str(), "Sparse matrix")
+        .def("__iter__", [](SparseMatrix<ValueType>& matrix) {
                 return py::make_iterator(matrix.begin(), matrix.end());
             }, py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)
-        .def("__str__", &streamToString<SparseMatrix<double>>)
-        .def_property_readonly("nr_rows", &SparseMatrix<double>::getRowCount, "Number of rows")
-        .def_property_readonly("nr_columns", &SparseMatrix<double>::getColumnCount, "Number of columns")
-        .def_property_readonly("nr_entries", &SparseMatrix<double>::getEntryCount, "Number of non-zero entries")
-        .def_property_readonly("_row_group_indices", &SparseMatrix<double>::getRowGroupIndices, "Starting rows of row groups")
-
-        .def("get_row_group_start", [](SparseMatrix<double>& matrix, entry_index<double> row) {return matrix.getRowGroupIndices()[row];})
-        .def("get_row_group_end", [](SparseMatrix<double>& matrix, entry_index<double> row) {return matrix.getRowGroupIndices()[row+1];})
-        .def_property_readonly("has_trivial_row_grouping", &SparseMatrix<double>::hasTrivialRowGrouping, "Trivial row grouping")
-        .def("get_row", [](SparseMatrix<double>& matrix, entry_index<double> row) {
+        .def("__str__", &streamToString<SparseMatrix<ValueType>>)
+        .def_property_readonly("nr_rows", &SparseMatrix<ValueType>::getRowCount, "Number of rows")
+        .def_property_readonly("nr_columns", &SparseMatrix<ValueType>::getColumnCount, "Number of columns")
+        .def_property_readonly("nr_entries", &SparseMatrix<ValueType>::getEntryCount, "Number of non-zero entries")
+        .def_property_readonly("_row_group_indices", &SparseMatrix<ValueType>::getRowGroupIndices, "Starting rows of row groups")
+
+        .def("get_row_group_start", [](SparseMatrix<ValueType>& matrix, entry_index<ValueType> row) {return matrix.getRowGroupIndices()[row];})
+        .def("get_row_group_end", [](SparseMatrix<ValueType>& matrix, entry_index<ValueType> row) {return matrix.getRowGroupIndices()[row+1];})
+        .def_property_readonly("has_trivial_row_grouping", &SparseMatrix<ValueType>::hasTrivialRowGrouping, "Trivial row grouping")
+        .def("get_row", [](SparseMatrix<ValueType>& matrix, entry_index<ValueType> row) {
                 return matrix.getRows(row, row+1);
             }, py::return_value_policy::reference, py::keep_alive<1, 0>(), py::arg("row"), "Get row")
-        .def("get_rows", [](SparseMatrix<double>& matrix, entry_index<double> start, entry_index<double> end) {
+        .def("get_rows", [](SparseMatrix<ValueType>& matrix, entry_index<ValueType> start, entry_index<ValueType> end) {
                 return matrix.getRows(start, end);
             }, py::return_value_policy::reference, py::keep_alive<1, 0>(), py::arg("row_start"), py::arg("row_end"), "Get rows from start to end")
-        .def("print_row", [](SparseMatrix<double> const& matrix, entry_index<double> row) {
+        .def("print_row", [](SparseMatrix<ValueType> const& matrix, entry_index<ValueType> row) {
                 std::stringstream stream;
                 auto rows = matrix.getRows(row, row+1);
                 for (auto transition : rows) {
@@ -126,22 +92,22 @@ void define_sparse_matrix(py::module& m) {
                 }
                 return stream.str();
             }, py::arg("row"), "Print rows from start to end")
-        .def("submatrix", [](SparseMatrix<double> const& matrix, storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint, bool insertDiagonalEntries = false) {
+        .def("submatrix", [](SparseMatrix<ValueType> const& matrix, storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint, bool insertDiagonalEntries = false) {
                 return matrix.getSubmatrix(true, rowConstraint, columnConstraint, insertDiagonalEntries);
             }, py::arg("row_constraint"), py::arg("column_constraint"), py::arg("insert_diagonal_entries") = false, "Get submatrix")
         // Entry_index lead to problems
-        .def("row_iter", [](SparseMatrix<double>& matrix, row_index start, row_index end) {
+        .def("row_iter", [](SparseMatrix<ValueType>& matrix, row_index start, row_index end) {
                 return py::make_iterator(matrix.begin(start), matrix.end(end));
             }, py::keep_alive<0, 1>() /* keep object alive while iterator exists */, py::arg("row_start"), py::arg("row_end"), "Get iterator from start to end")
 
         // (partial) container interface to allow e.g. matrix[7:9]
-        .def("__len__", &SparseMatrix<double>::getRowCount)
-        .def("__getitem__", [](SparseMatrix<double>& matrix, entry_index<double> i) {
+        .def("__len__", &SparseMatrix<ValueType>::getRowCount)
+        .def("__getitem__", [](SparseMatrix<ValueType>& matrix, entry_index<ValueType> i) {
                 if (i >= matrix.getRowCount())
                     throw py::index_error();
                 return matrix.getRows(i, i+1);
             }, py::return_value_policy::reference, py::keep_alive<1, 0>())
-        .def("__getitem__", [](SparseMatrix<double>& matrix, py::slice slice) {
+        .def("__getitem__", [](SparseMatrix<ValueType>& matrix, py::slice slice) {
                 size_t start, stop, step, slice_length;
                 if (!slice.compute(matrix.getRowCount(), &start, &stop, &step, &slice_length))
                     throw py::error_already_set();
@@ -151,75 +117,18 @@ void define_sparse_matrix(py::module& m) {
             }, py::return_value_policy::reference, py::keep_alive<1, 0>())
     ;
 
-    m.def("_topological_sort_double", [](SparseMatrix<double>& matrix, std::vector<uint64_t> initial) { return storm::utility::graph::getTopologicalSort(matrix, initial); }, "matrix"_a, "initial"_a,  "get topological sort w.r.t. a transition matrix");
-    m.def("_topological_sort_rf", [](SparseMatrix<storm::RationalFunction>& matrix, std::vector<uint64_t> initial) { return storm::utility::graph::getTopologicalSort(matrix, initial); }, "matrix"_a, "initial"_a,  "get topological sort w.r.t. a transition matrix");
-
-
-    py::class_<SparseMatrix<RationalFunction>>(m, "ParametricSparseMatrix", "Parametric sparse matrix")
-        .def("__iter__", [](SparseMatrix<RationalFunction>& matrix) {
-                return py::make_iterator(matrix.begin(), matrix.end());
-            }, py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)
-        .def("__str__", &streamToString<SparseMatrix<RationalFunction>>)
-        .def_property_readonly("nr_rows", &SparseMatrix<RationalFunction>::getRowCount, "Number of rows")
-        .def_property_readonly("nr_columns", &SparseMatrix<RationalFunction>::getColumnCount, "Number of columns")
-        .def_property_readonly("nr_entries", &SparseMatrix<RationalFunction>::getEntryCount, "Number of non-zero entries")
-        .def_property_readonly("_row_group_indices", &SparseMatrix<RationalFunction>::getRowGroupIndices, "Starting rows of row groups")
-        .def("get_row_group_start", [](SparseMatrix<RationalFunction>& matrix, entry_index<RationalFunction> row) {return matrix.getRowGroupIndices()[row];})
-        .def("get_row_group_end", [](SparseMatrix<RationalFunction>& matrix, entry_index<RationalFunction> row) {return matrix.getRowGroupIndices()[row+1];})
-        .def_property_readonly("has_trivial_row_grouping", &SparseMatrix<RationalFunction>::hasTrivialRowGrouping, "Trivial row grouping")
-        .def("get_row", [](SparseMatrix<RationalFunction>& matrix, entry_index<RationalFunction> row) {
-                return matrix.getRows(row, row+1);
-            }, py::return_value_policy::reference, py::keep_alive<1, 0>(), py::arg("row"), "Get row")
-        .def("get_rows", [](SparseMatrix<RationalFunction>& matrix, entry_index<storm::RationalFunction> start, entry_index<storm::RationalFunction> end) {
-                return matrix.getRows(start, end);
-            }, py::return_value_policy::reference, py::keep_alive<1, 0>(), py::arg("row_start"), py::arg("row_end"), "Get rows from start to end")
-        .def("print_row", [](SparseMatrix<RationalFunction> const& matrix, entry_index<storm::RationalFunction> row) {
-                std::stringstream stream;
-                auto rows = matrix.getRows(row, row+1);
-                for (auto transition : rows) {
-                    stream << transition << ", ";
-                }
-                return stream.str();
-            }, py::arg("row"), "Print row")
-        .def("submatrix", [](SparseMatrix<RationalFunction> const& matrix, storm::storage::BitVector const& rowConstraint, storm::storage::BitVector const& columnConstraint, bool insertDiagonalEntries = false) {
-                return matrix.getSubmatrix(true, rowConstraint, columnConstraint, insertDiagonalEntries);
-            }, py::arg("row_constraint"), py::arg("column_constraint"), py::arg("insert_diagonal_entries") = false, "Get submatrix")
-        // Entry_index lead to problems
-        .def("row_iter", [](SparseMatrix<RationalFunction>& matrix, row_index start, row_index end) {
-                return py::make_iterator(matrix.begin(start), matrix.end(end));
-            }, py::keep_alive<0, 1>() /* keep object alive while iterator exists */, py::arg("row_start"), py::arg("row_end"), "Get iterator from start to end")
-
-        // (partial) container interface to allow e.g. matrix[7:9]
-        .def("__len__", &SparseMatrix<RationalFunction>::getRowCount)
-        .def("__getitem__", [](SparseMatrix<RationalFunction>& matrix, entry_index<RationalFunction> i) {
-                if (i >= matrix.getRowCount())
-                    throw py::index_error();
-                return matrix.getRows(i, i+1);
-            }, py::return_value_policy::reference, py::keep_alive<1, 0>())
-        .def("__getitem__", [](SparseMatrix<RationalFunction>& matrix, py::slice slice) {
-                size_t start, stop, step, slice_length;
-                if (!slice.compute(matrix.getRowCount(), &start, &stop, &step, &slice_length))
-                    throw py::error_already_set();
-                if (step != 1)
-                    throw py::value_error(); // not supported
-                return matrix.getRows(start, stop);
-            }, py::return_value_policy::reference, py::keep_alive<1, 0>())
-    ;
 
     // Rows
-    py::class_<SparseMatrix<double>::rows>(m, "SparseMatrixRows", "Set of rows in a sparse matrix")
-        .def("__iter__", [](SparseMatrix<double>::rows& rows) {
-                return py::make_iterator(rows.begin(), rows.end());
-            }, py::keep_alive<0, 1>())
-        .def("__str__", &containerToString<SparseMatrix<double>::rows>)
-        .def("__len__", &storm::storage::SparseMatrix<double>::rows::getNumberOfEntries)
-    ;
-
-    py::class_<SparseMatrix<RationalFunction>::rows>(m, "ParametricSparseMatrixRows", "Set of rows in a parametric sparse matrix")
-        .def("__iter__", [](SparseMatrix<RationalFunction>::rows& rows) {
+    py::class_<typename SparseMatrix<ValueType>::rows>(m, (vtSuffix + "SparseMatrixRows").c_str(), "Set of rows in a sparse matrix")
+        .def("__iter__", [](typename SparseMatrix<ValueType>::rows& rows) {
                 return py::make_iterator(rows.begin(), rows.end());
             }, py::keep_alive<0, 1>())
-        .def("__str__", &containerToString<SparseMatrix<RationalFunction>::rows>)
-        .def("__len__", &storm::storage::SparseMatrix<storm::RationalFunction>::rows::getNumberOfEntries)
+        .def("__str__", &containerToString<typename SparseMatrix<ValueType>::rows>)
+        .def("__len__", &storm::storage::SparseMatrix<ValueType>::rows::getNumberOfEntries)
     ;
 }
+
+template void define_sparse_matrix<double>(py::module& m, std::string const& vtSuffix);
+template void define_sparse_matrix<storm::RationalNumber>(py::module& m, std::string const& vtSuffix);
+template void define_sparse_matrix<storm::RationalFunction>(py::module& m, std::string const& vtSuffix);
+
diff --git a/src/storage/matrix.h b/src/storage/matrix.h
index 96e3df6..d6f3776 100644
--- a/src/storage/matrix.h
+++ b/src/storage/matrix.h
@@ -3,6 +3,9 @@
 
 #include "common.h"
 
-void define_sparse_matrix(py::module& m);
+template<typename ValueType>
+void define_sparse_matrix(py::module& m, std::string const& vtSuffix);
+
+void define_sparse_matrix_nt(py::module& m);
 
 #endif /* PYTHON_STORAGE_MATRIX_H_ */
diff --git a/src/storage/model.cpp b/src/storage/model.cpp
index 152c140..dee8661 100644
--- a/src/storage/model.cpp
+++ b/src/storage/model.cpp
@@ -115,12 +115,18 @@ void define_model(py::module& m) {
         .def("_as_sparse_dtmc", [](ModelBase &modelbase) {
                 return modelbase.as<SparseDtmc<double>>();
             }, "Get model as sparse DTMC")
+        .def("_as_sparse_exact_dtmc", [](ModelBase &modelbase) {
+                return modelbase.as<SparseDtmc<storm::RationalNumber>>();
+            }, "Get model as sparse DTMC")
         .def("_as_sparse_pdtmc", [](ModelBase &modelbase) {
                 return modelbase.as<SparseDtmc<RationalFunction>>();
             }, "Get model as sparse pDTMC")
         .def("_as_sparse_mdp", [](ModelBase &modelbase) {
                 return modelbase.as<SparseMdp<double>>();
             }, "Get model as sparse MDP")
+        .def("_as_sparse_exact_mdp", [](ModelBase &modelbase) {
+                return modelbase.as<SparseMdp<storm::RationalNumber>>();
+            }, "Get model as sparse exact MDP")
         .def("_as_sparse_pmdp", [](ModelBase &modelbase) {
                 return modelbase.as<SparseMdp<RationalFunction>>();
             }, "Get model as sparse pMDP")
@@ -171,90 +177,94 @@ void define_model(py::module& m) {
 
 
 // Bindings for sparse models
-void define_sparse_model(py::module& m) {
+template<typename ValueType>
+void define_sparse_model(py::module& m, std::string const& vtSuffix) {
 
     // Models with double numbers
-    py::class_<SparseModel<double>, std::shared_ptr<SparseModel<double>>, ModelBase> model(m, "_SparseModel", "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix");
-    model.def_property_readonly("labeling", &getLabeling<double>, "Labels")
-        .def("has_choice_labeling", [](SparseModel<double> const& model) {return model.hasChoiceLabeling();}, "Does the model have an associated choice labelling?")
-        .def_property_readonly("choice_labeling", [](SparseModel<double> const& model) {return model.getChoiceLabeling();}, "get choice labelling")
-        .def("has_choice_origins", [](SparseModel<double> const& model) {return model.hasChoiceOrigins();}, "has choice origins?")
-        .def_property_readonly("choice_origins", [](SparseModel<double> const& model) {return model.getChoiceOrigins();})
-        .def("labels_state", &SparseModel<double>::getLabelsOfState, py::arg("state"), "Get labels of state")
-        .def_property_readonly("initial_states", &getSparseInitialStates<double>, "Initial states")
-        .def_property_readonly("states", [](SparseModel<double>& model) {
-                return SparseModelStates<double>(model);
+    py::class_<SparseModel<ValueType>, std::shared_ptr<SparseModel<ValueType>>, ModelBase> model(m, ("_Sparse" + vtSuffix + "Model").c_str(),
+                                                                                           "A probabilistic model where transitions are represented by doubles and saved in a sparse matrix");
+    model.def_property_readonly("labeling", &getLabeling<ValueType>, "Labels")
+        .def("has_choice_labeling", [](SparseModel<ValueType> const& model) {return model.hasChoiceLabeling();}, "Does the model have an associated choice labelling?")
+        .def_property_readonly("choice_labeling", [](SparseModel<ValueType> const& model) {return model.getChoiceLabeling();}, "get choice labelling")
+        .def("has_choice_origins", [](SparseModel<ValueType> const& model) {return model.hasChoiceOrigins();}, "has choice origins?")
+        .def_property_readonly("choice_origins", [](SparseModel<ValueType> const& model) {return model.getChoiceOrigins();})
+        .def("labels_state", &SparseModel<ValueType>::getLabelsOfState, py::arg("state"), "Get labels of state")
+        .def_property_readonly("initial_states", &getSparseInitialStates<ValueType>, "Initial states")
+        .def_property_readonly("states", [](SparseModel<ValueType>& model) {
+                return SparseModelStates<ValueType>(model);
             }, "Get states")
-        .def_property_readonly("reward_models", [](SparseModel<double>& model) {return model.getRewardModels(); }, "Reward models")
-        .def_property_readonly("transition_matrix", &getTransitionMatrix<double>, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Transition matrix")
-        .def_property_readonly("backward_transition_matrix", &SparseModel<double>::getBackwardTransitions, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix")
-        .def("get_reward_model", [](SparseModel<double>& model, std::string const& name) {return model.getRewardModel(name);}, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Reward model")
-        .def("has_state_valuations", [](SparseModel<double> const& model) {return model.hasStateValuations();}, "has state valuation?")
-        .def_property_readonly("state_valuations",  [](SparseModel<double> const& model) {return model.getStateValuations();}, "state valuations")
-        .def("reduce_to_state_based_rewards", &SparseModel<double>::reduceToStateBasedRewards)
-        .def("is_sink_state", &SparseModel<double>::isSinkState, py::arg("state"))
+        .def_property_readonly("reward_models", [](SparseModel<ValueType>& model) {return model.getRewardModels(); }, "Reward models")
+        .def_property_readonly("transition_matrix", &getTransitionMatrix<ValueType>, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Transition matrix")
+        .def_property_readonly("backward_transition_matrix", &SparseModel<ValueType>::getBackwardTransitions, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Backward transition matrix")
+        .def("get_reward_model", [](SparseModel<ValueType>& model, std::string const& name) {return model.getRewardModel(name);}, py::return_value_policy::reference, py::keep_alive<1, 0>(), "Reward model")
+        .def("has_state_valuations", [](SparseModel<ValueType> const& model) {return model.hasStateValuations();}, "has state valuation?")
+        .def_property_readonly("state_valuations",  [](SparseModel<ValueType> const& model) {return model.getStateValuations();}, "state valuations")
+        .def("reduce_to_state_based_rewards", &SparseModel<ValueType>::reduceToStateBasedRewards)
+        .def("is_sink_state", &SparseModel<ValueType>::isSinkState, py::arg("state"))
         .def("__str__", &getModelInfoPrinter)
-        .def("to_dot", [](SparseModel<double>& model) { std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }, "Write dot to a string")
+        .def("to_dot", [](SparseModel<ValueType>& model) { std::stringstream ss; model.writeDotToStream(ss); return ss.str(); }, "Write dot to a string")
     ;
-    py::class_<SparseDtmc<double>, std::shared_ptr<SparseDtmc<double>>>(m, "SparseDtmc", "DTMC in sparse representation", model)
-        .def(py::init<SparseDtmc<double>>(), py::arg("other_model"))
-        .def(py::init<ModelComponents<double> const&>(), py::arg("components"))
+    py::class_<SparseDtmc<ValueType>, std::shared_ptr<SparseDtmc<ValueType>>>(m, ("Sparse" + vtSuffix + "Dtmc").c_str(), "DTMC in sparse representation", model)
+        .def(py::init<SparseDtmc<ValueType>>(), py::arg("other_model"))
+        .def(py::init<ModelComponents<ValueType> const&>(), py::arg("components"))
         .def("__str__", &getModelInfoPrinter)
     ;
-    py::class_<SparseMdp<double>, std::shared_ptr<SparseMdp<double>>> mdp(m, "SparseMdp", "MDP in sparse representation", model);
-    mdp.def(py::init<SparseMdp<double>>(), py::arg("other_model"))
-        .def(py::init<ModelComponents<double> const&, storm::models::ModelType>(), py::arg("components"), py::arg("type")=storm::models::ModelType::Mdp)
-        .def_property_readonly("nondeterministic_choice_indices", [](SparseMdp<double> const& mdp) { return mdp.getNondeterministicChoiceIndices(); })
-        .def("get_nr_available_actions", [](SparseMdp<double> const& mdp, uint64_t stateIndex) { return mdp.getNondeterministicChoiceIndices()[stateIndex+1] - mdp.getNondeterministicChoiceIndices()[stateIndex] ; }, py::arg("state"))
-        .def("get_choice_index", [](SparseMdp<double> const& mdp, uint64_t state, uint64_t actOff) { return mdp.getNondeterministicChoiceIndices()[state]+actOff; }, py::arg("state"), py::arg("action_offset"), "gets the choice index for the offset action from the given state.")
-        .def("apply_scheduler", [](SparseMdp<double> const& mdp, storm::storage::Scheduler<double> const& scheduler, bool dropUnreachableStates) { return mdp.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true)
+    py::class_<SparseMdp<ValueType>, std::shared_ptr<SparseMdp<ValueType>>> mdp(m, ("Sparse" + vtSuffix + "Mdp").c_str(), "MDP in sparse representation", model);
+    mdp.def(py::init<SparseMdp<ValueType>>(), py::arg("other_model"))
+        .def(py::init<ModelComponents<ValueType> const&, storm::models::ModelType>(), py::arg("components"), py::arg("type")=storm::models::ModelType::Mdp)
+        .def_property_readonly("nondeterministic_choice_indices", [](SparseMdp<ValueType> const& mdp) { return mdp.getNondeterministicChoiceIndices(); })
+        .def("get_nr_available_actions", [](SparseMdp<ValueType> const& mdp, uint64_t stateIndex) { return mdp.getNondeterministicChoiceIndices()[stateIndex+1] - mdp.getNondeterministicChoiceIndices()[stateIndex] ; }, py::arg("state"))
+        .def("get_choice_index", [](SparseMdp<ValueType> const& mdp, uint64_t state, uint64_t actOff) { return mdp.getNondeterministicChoiceIndices()[state]+actOff; }, py::arg("state"), py::arg("action_offset"), "gets the choice index for the offset action from the given state.")
+        .def("apply_scheduler", [](SparseMdp<ValueType> const& mdp, storm::storage::Scheduler<ValueType> const& scheduler, bool dropUnreachableStates) { return mdp.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true)
         .def("__str__", &getModelInfoPrinter)
     ;
-    py::class_<SparsePomdp<double>, std::shared_ptr<SparsePomdp<double>>>(m, "SparsePomdp", "POMDP in sparse representation", mdp)
-        .def(py::init<SparsePomdp<double>>(), py::arg("other_model"))
-        .def(py::init<ModelComponents<double> const&, bool>(), py::arg("components"), py::arg("canonic_flag")=false)
+    py::class_<SparsePomdp<ValueType>, std::shared_ptr<SparsePomdp<ValueType>>>(m, ("Sparse" + vtSuffix + "Pomdp").c_str(), "POMDP in sparse representation", mdp)
+        .def(py::init<SparsePomdp<ValueType>>(), py::arg("other_model"))
+        .def(py::init<ModelComponents<ValueType> const&, bool>(), py::arg("components"), py::arg("canonic_flag")=false)
         .def("__str__", &getModelInfoPrinter)
-        .def("get_observation", &SparsePomdp<double>::getObservation, py::arg("state"))
-        .def_property_readonly("observations", &SparsePomdp<double>::getObservations)
-        .def_property_readonly("nr_observations", &SparsePomdp<double>::getNrObservations)
-        .def("has_observation_valuations", &SparsePomdp<double>::hasObservationValuations)
-        .def_property_readonly("observation_valuations", &SparsePomdp<double>::getObservationValuations)
+        .def("get_observation", &SparsePomdp<ValueType>::getObservation, py::arg("state"))
+        .def_property_readonly("observations", &SparsePomdp<ValueType>::getObservations)
+        .def_property_readonly("nr_observations", &SparsePomdp<ValueType>::getNrObservations)
+        .def("has_observation_valuations", &SparsePomdp<ValueType>::hasObservationValuations)
+        .def_property_readonly("observation_valuations", &SparsePomdp<ValueType>::getObservationValuations)
     ;
-    py::class_<SparseCtmc<double>, std::shared_ptr<SparseCtmc<double>>>(m, "SparseCtmc", "CTMC in sparse representation", model)
-        .def(py::init<SparseCtmc<double>>(), py::arg("other_model"))
-        .def(py::init<ModelComponents<double> const&>(), py::arg("components"))
-        .def_property_readonly("exit_rates", [](SparseCtmc<double> const& ctmc) { return ctmc.getExitRateVector(); })
+    py::class_<SparseCtmc<ValueType>, std::shared_ptr<SparseCtmc<ValueType>>>(m, ("Sparse" + vtSuffix + "Ctmc").c_str(), "CTMC in sparse representation", model)
+        .def(py::init<SparseCtmc<ValueType>>(), py::arg("other_model"))
+        .def(py::init<ModelComponents<ValueType> const&>(), py::arg("components"))
+        .def_property_readonly("exit_rates", [](SparseCtmc<ValueType> const& ctmc) { return ctmc.getExitRateVector(); })
         .def("__str__", &getModelInfoPrinter)
     ;
-    py::class_<SparseMarkovAutomaton<double>, std::shared_ptr<SparseMarkovAutomaton<double>>>(m, "SparseMA", "MA in sparse representation", model)
-        .def(py::init<SparseMarkovAutomaton<double>>(), py::arg("other_model"))
-        .def(py::init<ModelComponents<double> const&>(), py::arg("components"))
-        .def_property_readonly("exit_rates", [](SparseMarkovAutomaton<double> const& ma) { return ma.getExitRates(); })
-        .def_property_readonly("markovian_states", [](SparseMarkovAutomaton<double> const& ma) { return ma.getMarkovianStates(); })
-        .def_property_readonly("nondeterministic_choice_indices", [](SparseMarkovAutomaton<double> const& ma) { return ma.getNondeterministicChoiceIndices(); })
-        .def("apply_scheduler", [](SparseMarkovAutomaton<double> const& ma, storm::storage::Scheduler<double> const& scheduler, bool dropUnreachableStates) { return ma.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true)
+    py::class_<SparseMarkovAutomaton<ValueType>, std::shared_ptr<SparseMarkovAutomaton<ValueType>>>(m, ("Sparse" + vtSuffix + "MA").c_str(), "MA in sparse representation", model)
+        .def(py::init<SparseMarkovAutomaton<ValueType>>(), py::arg("other_model"))
+        .def(py::init<ModelComponents<ValueType> const&>(), py::arg("components"))
+        .def_property_readonly("exit_rates", [](SparseMarkovAutomaton<ValueType> const& ma) { return ma.getExitRates(); })
+        .def_property_readonly("markovian_states", [](SparseMarkovAutomaton<ValueType> const& ma) { return ma.getMarkovianStates(); })
+        .def_property_readonly("nondeterministic_choice_indices", [](SparseMarkovAutomaton<ValueType> const& ma) { return ma.getNondeterministicChoiceIndices(); })
+        .def("apply_scheduler", [](SparseMarkovAutomaton<ValueType> const& ma, storm::storage::Scheduler<ValueType> const& scheduler, bool dropUnreachableStates) { return ma.applyScheduler(scheduler, dropUnreachableStates); } , "apply scheduler", "scheduler"_a, "drop_unreachable_states"_a = true)
         .def("__str__", &getModelInfoPrinter)
-        .def_property_readonly("convertible_to_ctmc", &SparseMarkovAutomaton<double>::isConvertibleToCtmc, "Check whether the MA can be converted into a CTMC.")
-        .def("convert_to_ctmc", &SparseMarkovAutomaton<double>::convertToCtmc, "Convert the MA into a CTMC.")
+        .def_property_readonly("convertible_to_ctmc", &SparseMarkovAutomaton<ValueType>::isConvertibleToCtmc, "Check whether the MA can be converted into a CTMC.")
+        .def("convert_to_ctmc", &SparseMarkovAutomaton<ValueType>::convertToCtmc, "Convert the MA into a CTMC.")
     ;
 
-    py::class_<SparseRewardModel<double>>(m, "SparseRewardModel", "Reward structure for sparse models")
-        .def(py::init<boost::optional<std::vector<double>> const&, boost::optional<std::vector<double>> const&,
-                boost::optional<storm::storage::SparseMatrix<double>> const&>(), py::arg("optional_state_reward_vector") = boost::none,
+    py::class_<SparseRewardModel<ValueType>>(m, ("Sparse" + vtSuffix + "RewardModel").c_str(), "Reward structure for sparse models")
+        .def(py::init<boost::optional<std::vector<ValueType>> const&, boost::optional<std::vector<ValueType>> const&,
+                boost::optional<storm::storage::SparseMatrix<ValueType>> const&>(), py::arg("optional_state_reward_vector") = boost::none,
                 py::arg("optional_state_action_reward_vector") = boost::none,  py::arg("optional_transition_reward_matrix") = boost::none)
-        .def_property_readonly("has_state_rewards", &SparseRewardModel<double>::hasStateRewards)
-        .def_property_readonly("has_state_action_rewards", &SparseRewardModel<double>::hasStateActionRewards)
-        .def_property_readonly("has_transition_rewards", &SparseRewardModel<double>::hasTransitionRewards)
-        .def_property_readonly("transition_rewards", [](SparseRewardModel<double>& rewardModel) {return rewardModel.getTransitionRewardMatrix();})
-        .def_property_readonly("state_rewards", [](SparseRewardModel<double>& rewardModel) {return rewardModel.getStateRewardVector();})
-        .def("get_state_reward", [](SparseRewardModel<double>& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);})
-        .def("get_zero_reward_states", &SparseRewardModel<double>::getStatesWithZeroReward<double>, "get states where all rewards are zero", py::arg("transition_matrix"))
-        .def("get_state_action_reward", [](SparseRewardModel<double>& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);})
-        .def_property_readonly("state_action_rewards", [](SparseRewardModel<double>& rewardModel) {return rewardModel.getStateActionRewardVector();})
-        .def("reduce_to_state_based_rewards", [](SparseRewardModel<double>& rewardModel, storm::storage::SparseMatrix<double> const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);},  py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards")
+        .def_property_readonly("has_state_rewards", &SparseRewardModel<ValueType>::hasStateRewards)
+        .def_property_readonly("has_state_action_rewards", &SparseRewardModel<ValueType>::hasStateActionRewards)
+        .def_property_readonly("has_transition_rewards", &SparseRewardModel<ValueType>::hasTransitionRewards)
+        .def_property_readonly("transition_rewards", [](SparseRewardModel<ValueType>& rewardModel) {return rewardModel.getTransitionRewardMatrix();})
+        .def_property_readonly("state_rewards", [](SparseRewardModel<ValueType>& rewardModel) {return rewardModel.getStateRewardVector();})
+        .def("get_state_reward", [](SparseRewardModel<ValueType>& rewardModel, uint64_t state) {return rewardModel.getStateReward(state);})
+        .def("get_zero_reward_states", &SparseRewardModel<ValueType>::template getStatesWithZeroReward<ValueType>, "get states where all rewards are zero", py::arg("transition_matrix"))
+        .def("get_state_action_reward", [](SparseRewardModel<ValueType>& rewardModel, uint64_t action_index) {return rewardModel.getStateActionReward(action_index);})
+        .def_property_readonly("state_action_rewards", [](SparseRewardModel<ValueType>& rewardModel) {return rewardModel.getStateActionRewardVector();})
+        .def("reduce_to_state_based_rewards", [](SparseRewardModel<ValueType>& rewardModel, storm::storage::SparseMatrix<ValueType> const& transitions, bool onlyStateRewards){return rewardModel.reduceToStateBasedRewards(transitions, onlyStateRewards);},  py::arg("transition_matrix"), py::arg("only_state_rewards"), "Reduce to state-based rewards")
     ;
 
+}
 
+void define_sparse_parametric_model(py::module& m) {
     // Parametric models
     py::class_<SparseModel<RationalFunction>, std::shared_ptr<SparseModel<RationalFunction>>, ModelBase> modelRatFunc(m, "_SparseParametricModel", "A probabilistic model where transitions are represented by rational functions and saved in a sparse matrix");
     modelRatFunc.def("collect_probability_parameters", &probabilityVariables, "Collect parameters")
@@ -393,3 +403,5 @@ void define_symbolic_model(py::module& m, std::string vt_suffix) {
 }
 
 template void define_symbolic_model<storm::dd::DdType::Sylvan>(py::module& m, std::string vt_suffix);
+template void define_sparse_model<double>(py::module& m, std::string const& vt_suffix);
+template void define_sparse_model<storm::RationalNumber>(py::module& m, std::string const& vt_suffix);
diff --git a/src/storage/model.h b/src/storage/model.h
index 7643d39..ead0d7f 100644
--- a/src/storage/model.h
+++ b/src/storage/model.h
@@ -4,7 +4,9 @@
 #include "storm/storage/dd/DdType.h"
 
 void define_model(py::module& m);
-void define_sparse_model(py::module& m);
+template<typename ValueType>
+void define_sparse_model(py::module& m, std::string const& vtSuffix);
+void define_sparse_parametric_model(py::module& m);
 
 template<storm::dd::DdType DdType>
 void define_symbolic_model(py::module& m, std::string vt_suffix);
diff --git a/src/storage/model_components.cpp b/src/storage/model_components.cpp
index 228bb60..837615c 100644
--- a/src/storage/model_components.cpp
+++ b/src/storage/model_components.cpp
@@ -16,36 +16,39 @@ template<typename ValueType> using SparseRewardModel = storm::models::sparse::St
 template<typename ValueType> using SparseModelComponents = storm::storage::sparse::ModelComponents<ValueType>;
 
 
-// Parametric models, Valuetype: <storm::RationalFunction> todo
+template<typename ValueType>
+void define_sparse_model_components(py::module& m, std::string const& vtSuffix) {
 
-void define_sparse_model_components(py::module& m) {
+    py::class_<SparseModelComponents<ValueType>, std::shared_ptr<SparseModelComponents<ValueType>>>(m, ("Sparse" + vtSuffix + "ModelComponents").c_str(), "Components required for building a sparse model")
 
-    py::class_<SparseModelComponents<double>, std::shared_ptr<SparseModelComponents<double>>>(m, "SparseModelComponents", "Components required for building a sparse model")
-
-        .def(py::init<SparseMatrix<double> const&, StateLabeling const&, std::unordered_map<std::string, SparseRewardModel<double>> const&,
+        .def(py::init<SparseMatrix<ValueType> const&, StateLabeling const&, std::unordered_map<std::string, SparseRewardModel<ValueType>> const&,
             bool, boost::optional<BitVector> const&, boost::optional<SparseMatrix<storm::storage::sparse::state_type>> const&>(),
-            py::arg("transition_matrix") = SparseMatrix<double>(), py::arg("state_labeling") = storm::models::sparse::StateLabeling(),
-            py::arg("reward_models") =  std::unordered_map<std::string, SparseRewardModel<double>>(), py::arg("rate_transitions") = false,
+            py::arg("transition_matrix") = SparseMatrix<ValueType>(), py::arg("state_labeling") = storm::models::sparse::StateLabeling(),
+            py::arg("reward_models") =  std::unordered_map<std::string, SparseRewardModel<ValueType>>(), py::arg("rate_transitions") = false,
             py::arg("markovian_states") = boost::none, py::arg("player1_matrix") = boost::none)
 
         // General components (for all model types)
-        .def_readwrite("transition_matrix", &SparseModelComponents<double>::transitionMatrix, "The transition matrix")
-        .def_readwrite("state_labeling", &SparseModelComponents<double>::stateLabeling, "The state labeling")
-        .def_readwrite("reward_models", &SparseModelComponents<double>::rewardModels, "Reward models associated with the model")
-        .def_readwrite("choice_labeling", &SparseModelComponents<double>::choiceLabeling, "A list that stores a labeling for each choice")
-        .def_readwrite("state_valuations", &SparseModelComponents<double>::stateValuations, "A list that stores for each state to which variable valuation it belongs")
-        .def_readwrite("choice_origins", &SparseModelComponents<double>::choiceOrigins, "Stores for each choice from which parts of the input model description it originates")
+        .def_readwrite("transition_matrix", &SparseModelComponents<ValueType>::transitionMatrix, "The transition matrix")
+        .def_readwrite("state_labeling", &SparseModelComponents<ValueType>::stateLabeling, "The state labeling")
+        .def_readwrite("reward_models", &SparseModelComponents<ValueType>::rewardModels, "Reward models associated with the model")
+        .def_readwrite("choice_labeling", &SparseModelComponents<ValueType>::choiceLabeling, "A list that stores a labeling for each choice")
+        .def_readwrite("state_valuations", &SparseModelComponents<ValueType>::stateValuations, "A list that stores for each state to which variable valuation it belongs")
+        .def_readwrite("choice_origins", &SparseModelComponents<ValueType>::choiceOrigins, "Stores for each choice from which parts of the input model description it originates")
 
         // POMDP specific components
-        .def_readwrite("observability_classes", &SparseModelComponents<double>::observabilityClasses, "The POMDP observations")
+        .def_readwrite("observability_classes", &SparseModelComponents<ValueType>::observabilityClasses, "The POMDP observations")
 
         // Continuous time specific components (CTMCs, Markov Automata):
-        .def_readwrite("rate_transitions", &SparseModelComponents<double>::rateTransitions, "True iff the transition values (for Markovian choices) are interpreted as rates")
-        .def_readwrite("exit_rates", &SparseModelComponents<double>::exitRates, "The exit rate for each state. Must be given for CTMCs and MAs, if rate_transitions is false. Otherwise, it is optional.")
-        .def_readwrite("markovian_states", &SparseModelComponents<double>::markovianStates, "A list that stores which states are Markovian (only for Markov Automata)")
+        .def_readwrite("rate_transitions", &SparseModelComponents<ValueType>::rateTransitions, "True iff the transition values (for Markovian choices) are interpreted as rates")
+        .def_readwrite("exit_rates", &SparseModelComponents<ValueType>::exitRates, "The exit rate for each state. Must be given for CTMCs and MAs, if rate_transitions is false. Otherwise, it is optional.")
+        .def_readwrite("markovian_states", &SparseModelComponents<ValueType>::markovianStates, "A list that stores which states are Markovian (only for Markov Automata)")
 
         // Stochastic two player game specific components:
-        .def_readwrite("player1_matrix", &SparseModelComponents<double>::observabilityClasses, "Matrix of player 1 choices (needed for stochastic two player games")
+        .def_readwrite("player1_matrix", &SparseModelComponents<ValueType>::observabilityClasses, "Matrix of player 1 choices (needed for stochastic two player games")
         ;
 
 }
+
+
+template void define_sparse_model_components<double>(py::module& m, std::string const& vtSuffix);
+template void define_sparse_model_components<storm::RationalNumber>(py::module& m, std::string const& vtSuffix);
diff --git a/src/storage/model_components.h b/src/storage/model_components.h
index 14117a7..badc0c7 100644
--- a/src/storage/model_components.h
+++ b/src/storage/model_components.h
@@ -3,6 +3,7 @@
 
 #include "common.h"
 
-void define_sparse_model_components(py::module& m);
+template<typename ValueType>
+void define_sparse_model_components(py::module& m, std::string const& vtSuffix);
 
 #endif /* PYTHON_STORAGE_SPARSEMODELCOMPONENTS_H */
\ No newline at end of file
diff --git a/src/storage/scheduler.cpp b/src/storage/scheduler.cpp
index 97151c3..7480351 100644
--- a/src/storage/scheduler.cpp
+++ b/src/storage/scheduler.cpp
@@ -36,4 +36,5 @@ void define_scheduler(py::module& m, std::string vt_suffix) {
 }
 
 
-template void define_scheduler<double>(py::module& m, std::string vt_suffix);
\ No newline at end of file
+template void define_scheduler<double>(py::module& m, std::string vt_suffix);
+template void define_scheduler<storm::RationalNumber>(py::module& m, std::string vt_suffix);
\ No newline at end of file
diff --git a/src/storage/state.cpp b/src/storage/state.cpp
index b13c887..f5ca6cd 100644
--- a/src/storage/state.cpp
+++ b/src/storage/state.cpp
@@ -1,51 +1,39 @@
 #include "state.h"
 
-void define_state(py::module& m) {
+template<typename ValueType>
+void define_state(py::module& m, std::string const& vtSuffix) {
     
     // SparseModelStates
-    py::class_<SparseModelStates<double>>(m, "SparseModelStates", "States in sparse model")
-        .def("__getitem__", &SparseModelStates<double>::getState)
-        .def("__len__", &SparseModelStates<double>::getSize)
-    ;
-    py::class_<SparseModelStates<storm::RationalFunction>>(m, "SparseParametricModelStates", "States in sparse parametric model")
-        .def("__getitem__", &SparseModelStates<storm::RationalFunction>::getState)
-        .def("__len__", &SparseModelStates<storm::RationalFunction>::getSize)
+    py::class_<SparseModelStates<ValueType>>(m, ("Sparse" + vtSuffix + "ModelStates").c_str(), "States in sparse model")
+        .def("__getitem__", &SparseModelStates<ValueType>::getState)
+        .def("__len__", &SparseModelStates<ValueType>::getSize)
     ;
+
     // SparseModelState
-    py::class_<SparseModelState<double>>(m, "SparseModelState", "State in sparse model")
-        .def("__str__", &SparseModelState<double>::toString)
-        .def_property_readonly("id", &SparseModelState<double>::getIndex, "Id")
-        .def_property_readonly("labels", &SparseModelState<double>::getLabels, "Labels")
-        .def_property_readonly("actions", &SparseModelState<double>::getActions, "Get actions")
-        .def("__int__",&SparseModelState<double>::getIndex)
-    ;
-    py::class_<SparseModelState<storm::RationalFunction>>(m, "SparseParametricModelState", "State in sparse parametric model")
-        .def("__str__", &SparseModelState<storm::RationalFunction>::toString)
-        .def_property_readonly("id", &SparseModelState<storm::RationalFunction>::getIndex, "Id")
-        .def_property_readonly("labels", &SparseModelState<storm::RationalFunction>::getLabels, "Labels")
-        .def_property_readonly("actions", &SparseModelState<storm::RationalFunction>::getActions, "Get actions")
-        .def("__int__",&SparseModelState<storm::RationalFunction>::getIndex)
+    py::class_<SparseModelState<ValueType>>(m, ("Sparse" + vtSuffix + "ModelState").c_str(), "State in sparse model")
+        .def("__str__", &SparseModelState<ValueType>::toString)
+        .def_property_readonly("id", &SparseModelState<ValueType>::getIndex, "Id")
+        .def_property_readonly("labels", &SparseModelState<ValueType>::getLabels, "Labels")
+        .def_property_readonly("actions", &SparseModelState<ValueType>::getActions, "Get actions")
+        .def("__int__",&SparseModelState<ValueType>::getIndex)
     ;
-    
+
     // SparseModelActions
-    py::class_<SparseModelActions<double>>(m, "SparseModelActions", "Actions for state in sparse model")
-        .def("__getitem__", &SparseModelActions<double>::getAction)
-        .def("__len__", &SparseModelActions<double>::getSize)
-    ;
-    py::class_<SparseModelActions<storm::RationalFunction>>(m, "SparseParametricModelActions", "Actions for state in sparse parametric model")
-        .def("__getitem__", &SparseModelActions<storm::RationalFunction>::getAction)
-        .def("__len__", &SparseModelActions<storm::RationalFunction>::getSize)
+    py::class_<SparseModelActions<ValueType>>(m, ("Sparse" + vtSuffix + "ModelActions").c_str(), "Actions for state in sparse model")
+        .def("__getitem__", &SparseModelActions<ValueType>::getAction)
+        .def("__len__", &SparseModelActions<ValueType>::getSize)
     ;
+
     // SparseModelAction
-    py::class_<SparseModelAction<double>>(m, "SparseModelAction", "Action for state in sparse model")
-        .def("__str__", &SparseModelAction<double>::toString)
-        .def_property_readonly("id", &SparseModelAction<double>::getIndex, "Id")
-        .def_property_readonly("transitions", &SparseModelAction<double>::getTransitions, "Get transitions")
-    ;
-    py::class_<SparseModelAction<storm::RationalFunction>>(m, "SparseParametricModelAction", "Action for state in sparse parametric model")
-        .def("__str__", &SparseModelAction<storm::RationalFunction>::toString)
-        .def_property_readonly("id", &SparseModelAction<storm::RationalFunction>::getIndex, "Id")
-        .def_property_readonly("transitions", &SparseModelAction<storm::RationalFunction>::getTransitions, "Get transitions")
+    py::class_<SparseModelAction<ValueType>>(m, ("Sparse" + vtSuffix + "ModelAction").c_str(), "Action for state in sparse model")
+        .def("__str__", &SparseModelAction<ValueType>::toString)
+        .def_property_readonly("id", &SparseModelAction<ValueType>::getIndex, "Id")
+        .def_property_readonly("transitions", &SparseModelAction<ValueType>::getTransitions, "Get transitions")
     ;
 
 }
+
+template void define_state<double>(py::module& m, std::string const& vtSuffix);
+template void define_state<storm::RationalNumber>(py::module& m, std::string const& vtSuffix);
+template void define_state<storm::RationalFunction>(py::module& m, std::string const& vtSuffix);
+
diff --git a/src/storage/state.h b/src/storage/state.h
index 0e91a11..bbe84eb 100644
--- a/src/storage/state.h
+++ b/src/storage/state.h
@@ -125,6 +125,7 @@ class SparseModelActions {
         storm::models::sparse::Model<ValueType>& model;
 };
 
-void define_state(py::module& m);
+template<typename ValueType>
+void define_state(py::module& m, std::string const& vtSuffix);
 
 #endif /* PYTHON_STORAGE_STATE_H_ */