Repository: adaa-polsl/RuleKit-python (language: python)

Issue:
Contrast sets



Discussion / hints:

Fix patch:
diff --git a/rulekit/__init__.py b/rulekit/__init__.py
index 192b536..c49da49 100644
--- a/rulekit/__init__.py
+++ b/rulekit/__init__.py
@@ -1,5 +1,4 @@
+# pylint: disable=missing-module-docstring
 from .main import RuleKit
 
 __version__ = '1.6.0'
-
-RuleKit = RuleKit
\ No newline at end of file
diff --git a/rulekit/__main__.py b/rulekit/__main__.py
index c13b425..292359b 100644
--- a/rulekit/__main__.py
+++ b/rulekit/__main__.py
@@ -1,20 +1,23 @@
+"""Module defining CLI
+"""
 import sys
 import json
-from typing import List
 import urllib.request
 import os
 import glob
-from rulekit.experiment import ExperimentRunner
+from rulekit._experiment import _ExperimentRunner
 from rulekit import RuleKit
 
 dir_path = os.path.dirname(os.path.realpath(__file__))
 
+REPOSITORY_URL: str = 'https://api.github.com/repos/adaa-polsl/RuleKit'
 
-def download_rulekit_jar():
+
+def _download_rulekit_jar():
     release_version = 'latest'
-    current_rulekit_jars_files: List[str] = glob.glob(
+    current_rulekit_jars_files: list[str] = glob.glob(
         f"{dir_path}/jar/*-all.jar")
-    url = f"https://api.github.com/repos/adaa-polsl/RuleKit/releases/{release_version}"
+    url = f"{REPOSITORY_URL}/{release_version}"
     req = urllib.request.Request(url)
     req.add_header('Content-Type', 'application/json; charset=utf-8')
     response = urllib.request.urlopen(req)
@@ -25,20 +28,22 @@ def download_rulekit_jar():
     asset = list(
         filter(lambda asset: asset['name'].endswith('-all.jar'), assets))
     if len(asset) != 1:
-        raise Exception('Failed to fetch latest RuleKit release jar file.')
+        raise ValueError('Failed to fetch latest RuleKit release jar file.')
     asset = asset[0]
     download_link = asset['browser_download_url']
 
     if len(current_rulekit_jars_files) > 0:
-        old_files_names = list(
-            map(lambda path: os.path.basename(path), current_rulekit_jars_files))
-        tmp = input(
-            f'Old RuleKit jar file/files ({old_files_names}) detected, do you want to remove it/them? Type "yes" or "no"\n')
-        if tmp == 'yes':
+        old_files_names = [os.path.basename(path)
+                           for path in current_rulekit_jars_files]
+        user_input: str = input(
+            f'Old RuleKit jar file/files ({old_files_names}) detected,' +
+            'do you want to remove it/them? Type "yes" or "no"\n'
+        )
+        if user_input == 'yes':
             for old_file_path in current_rulekit_jars_files:
                 os.remove(old_file_path)
             print('Old files removed.')
-        elif tmp != 'no':
+        elif user_input != 'no':
             print('I will treat it as no')
     print(f'Downloading jar file: "{asset["name"]}" from: "{download_link}"')
 
@@ -51,15 +56,15 @@ def show_progress(block_num, block_size, total_size):
     print('Download finished!\nPackage is ready to use.')
 
 
-def main():
+def _main():
     if len(sys.argv) > 1 and sys.argv[1] == 'download_jar':
-        download_rulekit_jar()
+        _download_rulekit_jar()
     else:
         # use rulekit batch CLI
         rulekit = RuleKit()
         rulekit.init()
-        ExperimentRunner.run(sys.argv[1:])
+        _ExperimentRunner.run(sys.argv[1:])
 
 
 if __name__ == "__main__":
-    main()
+    _main()
diff --git a/rulekit/_experiment.py b/rulekit/_experiment.py
new file mode 100644
index 0000000..36ac3c0
--- /dev/null
+++ b/rulekit/_experiment.py
@@ -0,0 +1,20 @@
+"""Module containing class for running experiments using legacy Java RuleKit CLI. For more
+details see  `documentation \
+<https://github.com/adaa-polsl/RuleKit/wiki/7-Library-API#71-running-an-experiment
+"""
+from jpype import JClass
+
+
+class _ExperimentRunner:
+
+    @staticmethod
+    def run(args: list[str]):
+        """Run experiment using core Java RuleKit CLI
+
+        Args:
+            args (list[str]): args
+        """
+        ExperimentalConsole = JClass(  # pylint: disable=invalid-name
+            'adaa.analytics.rules.consoles.ExperimentalConsole'
+        )
+        ExperimentalConsole.main(args)
diff --git a/rulekit/helpers.py b/rulekit/_helpers.py
similarity index 52%
rename from rulekit/helpers.py
rename to rulekit/_helpers.py
index 3387cc6..1bd7960 100644
--- a/rulekit/helpers.py
+++ b/rulekit/_helpers.py
@@ -1,324 +1,388 @@
-from typing import Union, List, Any
-from jpype import JClass, JString, JObject, JArray, java, addClassPath
-from jpype.pickle import JPickler, JUnpickler
-import io
-import numpy as np
-import pandas as pd
-from .params import Measures
-from .rules import Rule
-
-
-def get_rule_generator(expert: bool = False) -> Any:
-    OperatorDocumentation = JClass(
-        'com.rapidminer.tools.documentation.OperatorDocumentation')
-    OperatorDescription = JClass('com.rapidminer.operator.OperatorDescription')
-    Mockito = JClass('org.mockito.Mockito')
-    path = 'adaa.analytics.rules.operator.'
-    if expert:
-        path += 'ExpertRuleGenerator'
-    else:
-        path += 'RuleGenerator'
-    RuleGenerator = JClass(path)
-    documentation = Mockito.mock(OperatorDocumentation.class_)
-    description = Mockito.mock(OperatorDescription.class_)
-    Mockito.when(documentation.getShortName()).thenReturn(JString(''), None)
-    Mockito.when(description.getOperatorDocumentation()
-                 ).thenReturn(documentation, None)
-    return RuleGenerator(description)
-
-
-class RuleGeneratorConfigurator:
-
-    def __init__(self, rule_generator):
-        self.rule_generator = rule_generator
-        self.LogRank = None
-
-    def configure(self,
-                  min_rule_covered: int = None,
-                  induction_measure: Measures = None,
-                  pruning_measure: Union[Measures, str] = None,
-                  voting_measure: Measures = None,
-                  max_growing: int = None,
-                  enable_pruning: bool = None,
-                  ignore_missing: bool = None,
-                  max_uncovered_fraction: float = None,
-                  select_best_candidate: bool = None,
-                  survival_time_attr: str = None,
-
-                  extend_using_preferred: bool = None,
-                  extend_using_automatic: bool = None,
-                  induce_using_preferred: bool = None,
-                  induce_using_automatic: bool = None,
-                  consider_other_classes: bool = None,
-                  preferred_attributes_per_rule: int = None,
-                  preferred_conditions_per_rule: int = None) -> Any:
-        self._configure_rule_generator(
-            min_rule_covered=min_rule_covered,
-            induction_measure=induction_measure,
-            pruning_measure=pruning_measure,
-            voting_measure=voting_measure,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate,
-            extend_using_preferred=extend_using_preferred,
-            extend_using_automatic=extend_using_automatic,
-            induce_using_preferred=induce_using_preferred,
-            induce_using_automatic=induce_using_automatic,
-            consider_other_classes=consider_other_classes,
-            preferred_conditions_per_rule=preferred_conditions_per_rule,
-            preferred_attributes_per_rule=preferred_attributes_per_rule
-        )
-        return self.rule_generator
-
-    def configure_expert_parameter(self, param_name: str, param_value: Any):
-        if param_value is None:
-            return
-        rules_list = java.util.ArrayList()
-        if isinstance(param_value, list) and len(param_value) > 0:
-            if isinstance(param_value[0], str):
-                for index, rule in enumerate(param_value):
-                    rule_name = f'{param_name[:-1]}-{index}'
-                    rules_list.add(
-                        JObject([rule_name, rule], JArray('java.lang.String', 1)))
-            elif isinstance(param_value[0], Rule):
-                for index, rule in enumerate(param_value):
-                    rule_name = f'{param_name[:-1]}-{index}'
-                    rules_list.add(
-                        JObject([rule_name, str(rule)], JArray('java.lang.String', 1)))
-            elif isinstance(param_value[0], tuple):
-                for index, rule in enumerate(param_value):
-                    rules_list.add(
-                        JObject([rule[0], rule[1]], JArray('java.lang.String', 1)))
-        self.rule_generator.setListParameter(param_name, rules_list)
-
-    def configure_simple_parameter(self, param_name: str, param_value: Any):
-        if param_value is not None:
-            if isinstance(param_value, bool):
-                param_value = (str(param_value)).lower()
-            elif not isinstance(param_value, str):
-                param_value = str(param_value)
-            self.rule_generator.setParameter(param_name, param_value)
-
-    def _configure_measure_parameter(self, param_name: str, param_value: Union[str, Measures]):
-        if param_value is not None:
-            if isinstance(param_value, Measures):
-                self.rule_generator.setParameter(
-                    param_name, param_value.value)
-            if isinstance(param_value, str):
-                self.rule_generator.setParameter(param_name, 'UserDefined')
-                self.rule_generator.setParameter(param_name, param_value)
-
-    def _configure_rule_generator(
-            self,
-            min_rule_covered: int = None,
-            induction_measure: Measures = None,
-            pruning_measure: Measures = None,
-            voting_measure: Measures = None,
-            max_growing: int = None,
-            enable_pruning: bool = None,
-            ignore_missing: bool = None,
-            max_uncovered_fraction: float = None,
-            select_best_candidate: bool = None,
-
-            extend_using_preferred: bool = None,
-            extend_using_automatic: bool = None,
-            induce_using_preferred: bool = None,
-            induce_using_automatic: bool = None,
-            consider_other_classes: bool = None,
-            preferred_conditions_per_rule: int = None,
-            preferred_attributes_per_rule: int = None):
-        if induction_measure == Measures.LogRank or pruning_measure == Measures.LogRank or voting_measure == Measures.LogRank:
-            self.LogRank = JClass('adaa.analytics.rules.logic.quality.LogRank')
-        self.configure_simple_parameter('min_rule_covered', min_rule_covered)
-        self.configure_simple_parameter('max_growing', max_growing)
-        self.configure_simple_parameter('enable_pruning', enable_pruning)
-        self.configure_simple_parameter(
-            'max_uncovered_fraction', max_uncovered_fraction)
-        self.configure_simple_parameter(
-            'select_best_candidate', select_best_candidate)
-
-        self.configure_simple_parameter(
-            'extend_using_preferred', extend_using_preferred)
-        self.configure_simple_parameter(
-            'extend_using_automatic', extend_using_automatic)
-        self.configure_simple_parameter(
-            'induce_using_preferred', induce_using_preferred)
-        self.configure_simple_parameter(
-            'induce_using_automatic', induce_using_automatic)
-        self.configure_simple_parameter(
-            'consider_other_classes', consider_other_classes)
-        self.configure_simple_parameter(
-            'preferred_conditions_per_rule', preferred_conditions_per_rule)
-        self.configure_simple_parameter(
-            'preferred_attributes_per_rule', preferred_attributes_per_rule)
-
-        self._configure_measure_parameter(
-            'induction_measure', induction_measure)
-        self._configure_measure_parameter('pruning_measure', pruning_measure)
-        self._configure_measure_parameter('voting_measure', voting_measure)
-
-
-def map_attributes_names(example_set, attributes_names: List[str]):
-    for index, name in enumerate(attributes_names):
-        example_set.getAttributes().get(f'att{index + 1}').setName(name)
-
-
-def set_survival_time(example_set, survival_time_attribute: str) -> object:
-    OperatorDocumentation = JClass(
-        'com.rapidminer.tools.documentation.OperatorDocumentation')
-    OperatorDescription = JClass('com.rapidminer.operator.OperatorDescription')
-    Mockito = JClass('org.mockito.Mockito')
-    ChangeAttributeRole = JClass(
-        'com.rapidminer.operator.preprocessing.filter.ChangeAttributeRole')
-
-    documentation = Mockito.mock(OperatorDocumentation.class_)
-    description = Mockito.mock(OperatorDescription.class_)
-    Mockito.when(documentation.getShortName()).thenReturn(JString(''), None)
-    Mockito.when(description.getOperatorDocumentation()
-                 ).thenReturn(documentation, None)
-    role_setter = ChangeAttributeRole(description)
-    role_setter.setParameter(
-        ChangeAttributeRole.PARAMETER_NAME, survival_time_attribute)
-    role_setter.setParameter(
-        ChangeAttributeRole.PARAMETER_TARGET_ROLE, "survival_time")
-    return role_setter.apply(example_set)
-
-
-def _fix_missing_values(column) -> Any:
-    for i in range(0, len(column.values)):
-        if column.values[i] == b'?':
-            column.values[i] = None
-
-
-def create_example_set(values, labels=None, numeric_labels=False, survival_time_attribute: str = None) -> object:
-    if labels is None:
-        labels = ['' if not numeric_labels else 0] * len(values)
-    attributes_names = None
-    label_name = None
-    if isinstance(values, pd.DataFrame):
-        attributes_names = values.columns.values
-        values = values.to_numpy()
-    if isinstance(labels, pd.Series):
-        label_name = labels.name
-        labels = labels.to_numpy()
-    values = JObject(values, JArray('java.lang.Object', 2))
-    labels = JObject(labels, JArray('java.lang.Object', 1))
-    ExampleSetFactory = JClass('com.rapidminer.example.ExampleSetFactory')
-    example_set = ExampleSetFactory.createExampleSet(values, labels)
-    if attributes_names is not None:
-        map_attributes_names(example_set, attributes_names)
-    if label_name is not None:
-        example_set.getAttributes().get('label').setName(label_name)
-    if survival_time_attribute is not None:
-        if survival_time_attribute == '':
-            survival_time_attribute = f'att{example_set.getAttributes().size()}'
-        example_set = set_survival_time(example_set, survival_time_attribute)
-    return example_set
-
-
-def create_sorted_example_set(values, labels=None, numeric_labels=False, survival_time_attribute: str = None) -> object:
-    example_set = create_example_set(
-        values, labels, numeric_labels, survival_time_attribute)
-    SortedExampleSet = JClass("com.rapidminer.example.set.SortedExampleSet")
-    sorted_example_set = SortedExampleSet(
-        example_set, example_set.getAttributes().getLabel(), SortedExampleSet.INCREASING
-    )
-    return sorted_example_set
-
-
-class PredictionResultMapper:
-
-    @staticmethod
-    def map_confidence(predicted_example_set, label_unique_values: list) -> np.ndarray:
-        confidence_attributes_names = list(
-            map(lambda val: f'confidence_{val}', label_unique_values))
-        prediction = []
-        row_reader = predicted_example_set.getExampleTable().getDataRowReader()
-        confidence_attributes = []
-        for name in confidence_attributes_names:
-            confidence_attributes.append(
-                predicted_example_set.getAttributes().get(name))
-        while row_reader.hasNext():
-            row = row_reader.next()
-            value = []
-            for attribute in confidence_attributes:
-                value.append(attribute.getValue(row))
-            prediction.append(np.array(value))
-        return np.array(prediction)
-
-    @staticmethod
-    def map(predicted_example_set) -> np.ndarray:
-        attribute = predicted_example_set.getAttributes().get('prediction')
-        if attribute.isNominal():
-            return PredictionResultMapper.map_to_nominal(predicted_example_set)
-        else:
-            return PredictionResultMapper.map_to_numerical(predicted_example_set)
-
-    @staticmethod
-    def map_to_nominal(predicted_example_set) -> np.ndarray:
-        prediction = []
-        row_reader = predicted_example_set.getExampleTable().getDataRowReader()
-        attribute = predicted_example_set.getAttributes().get('prediction')
-        label_mapping = attribute.getMapping()
-        while row_reader.hasNext():
-            row = row_reader.next()
-            value_index = row.get(attribute)
-            value = label_mapping.mapIndex(round(value_index))
-            prediction.append(value)
-        prediction = list(map(str, prediction))
-        return np.array(prediction).astype(np.unicode_)
-
-    @staticmethod
-    def map_to_numerical(predicted_example_set, remap: bool = True) -> np.ndarray:
-        prediction = []
-        row_reader = predicted_example_set.getExampleTable().getDataRowReader()
-        attribute = predicted_example_set.getAttributes().get('prediction')
-        label_mapping = predicted_example_set.getAttributes().getLabel().getMapping()
-        while row_reader.hasNext():
-            row = row_reader.next()
-            if remap:
-                value = int(attribute.getValue(row))
-                value = float(str(label_mapping.mapIndex(value)))
-            else:
-                value = float(attribute.getValue(row))
-            prediction.append(value)
-        return np.array(prediction)
-
-    @staticmethod
-    def map_survival(predicted_example_set) -> np.ndarray:
-        estimators = []
-        attribute = predicted_example_set.getAttributes().get("estimator")
-        example_set_iterator = predicted_example_set.iterator()
-        while example_set_iterator.hasNext():
-            example = example_set_iterator.next()
-            example_estimator = str(example.getValueAsString(attribute))
-            example_estimator = example_estimator.split(" ")
-            number_of_points, example_estimator[0] = example_estimator[0].split(
-                ":")
-            times = [float(example_estimator[i])
-                     for i in range(len(example_estimator) - 1) if i % 2 == 0]
-            probabilities = [float(example_estimator[i])
-                             for i in range(len(example_estimator)) if i % 2 != 0]
-            estimator = {'times': times, 'probabilities': probabilities}
-            estimators.append(estimator)
-        return np.array(estimators)
-
-
-class ModelSerializer:
-
-    @staticmethod
-    def serialize(real_model: object) -> bytes:
-        in_memory_file = io.BytesIO()
-        JPickler(in_memory_file).dump(real_model)
-        serialized_bytes = in_memory_file.getvalue()
-        in_memory_file.close()
-        return serialized_bytes
-
-    @staticmethod
-    def deserialize(serialized_bytes: bytes) -> object:
-        in_memory_file = io.BytesIO(serialized_bytes)
-        model = JUnpickler(in_memory_file).load()
-        in_memory_file.close()
-        return model
+"""Contains helper functions and classes
+"""
+from typing import Union, Any
+from warnings import warn
+import io
+from jpype import JClass, JString, JObject, JArray, java
+from jpype.pickle import JPickler, JUnpickler
+import numpy as np
+import pandas as pd
+
+from .params import (
+    Measures,
+    CONTRAST_ATTR_ROLE,
+    SURVIVAL_TIME_ATTR_ROLE
+)
+from .rules import Rule
+from .main import RuleKit
+
+
+def get_rule_generator(expert: bool = False) -> Any:
+    """Factory for Java RuleGenerator class object
+
+    Args:
+        expert (bool, optional): Whether expert induction is enables. Defaults to False.
+
+    Returns:
+        Any: RuleGenerator instance
+    """
+    RuleKit.init()
+    OperatorDocumentation = JClass(  # pylint: disable=invalid-name
+        'com.rapidminer.tools.documentation.OperatorDocumentation')
+    OperatorDescription = JClass(  # pylint: disable=invalid-name
+        'com.rapidminer.operator.OperatorDescription'
+    )
+    Mockito = JClass('org.mockito.Mockito')  # pylint: disable=invalid-name
+    path: str = 'adaa.analytics.rules.operator.'
+    if expert:
+        path += 'ExpertRuleGenerator'
+    else:
+        path += 'RuleGenerator'
+    RuleGenerator = JClass(path)  # pylint: disable=invalid-name
+    documentation = Mockito.mock(OperatorDocumentation.class_)
+    description = Mockito.mock(OperatorDescription.class_)
+    Mockito.when(documentation.getShortName()).thenReturn(JString(''), None)
+    Mockito.when(description.getOperatorDocumentation()
+                 ).thenReturn(documentation, None)
+    return RuleGenerator(description)
+
+
+class RuleGeneratorConfigurator:
+    """Class for configuring rule induction parameters
+    """
+
+    def __init__(self, rule_generator):
+        self.rule_generator = rule_generator
+        self.LogRank = None  # pylint: disable=invalid-name
+
+    def configure(self, **kwargs: dict[str, Any]) -> Any:
+        """Configures RuleGenerator instance with given induction parameters
+
+        Returns:
+            Any: configured RuleGenerator instance
+        """
+        self._configure_rule_generator(**kwargs)
+        return self.rule_generator
+
+    def _configure_expert_parameter(self, param_name: str, param_value: Any):
+        if param_value is None:
+            return
+        rules_list = java.util.ArrayList()
+        if isinstance(param_value, list) and len(param_value) > 0:
+            if isinstance(param_value[0], str):
+                for index, rule in enumerate(param_value):
+                    rule_name = f'{param_name[:-1]}-{index}'
+                    rules_list.add(
+                        JObject([rule_name, rule], JArray('java.lang.String', 1)))
+            elif isinstance(param_value[0], Rule):
+                for index, rule in enumerate(param_value):
+                    rule_name = f'{param_name[:-1]}-{index}'
+                    rules_list.add(
+                        JObject([rule_name, str(rule)], JArray('java.lang.String', 1)))
+            elif isinstance(param_value[0], tuple):
+                for index, rule in enumerate(param_value):
+                    rules_list.add(
+                        JObject([rule[0], rule[1]], JArray('java.lang.String', 1)))
+        self.rule_generator.setListParameter(param_name, rules_list)
+
+    def _configure_simple_parameter(self, param_name: str, param_value: Any):
+        if param_value is not None:
+            if isinstance(param_value, bool):
+                param_value = (str(param_value)).lower()
+            elif not isinstance(param_value, str):
+                param_value = str(param_value)
+            self.rule_generator.setParameter(param_name, param_value)
+
+    def _configure_measure_parameter(self, param_name: str, param_value: Union[str, Measures]):
+        if param_value is not None:
+            if isinstance(param_value, Measures):
+                self.rule_generator.setParameter(
+                    param_name, param_value.value)
+            if isinstance(param_value, str):
+                self.rule_generator.setParameter(param_name, 'UserDefined')
+                self.rule_generator.setParameter(param_name, param_value)
+
+    def _configure_rule_generator(self, **kwargs: dict[str, Any]):
+        if kwargs.get('min_rule_covered', None) is not None:
+            # backward compatibility
+            # TODO remove in version 2.0.0
+            warn(
+                '"min_rule_covered" parameter was renamed to "minsupp_new" and is now ' +
+                'deprecated, "minsupp_new" instead. "min_rule_covered" parameter will be removed' +
+                ' in next major version of the package. ',
+                DeprecationWarning,
+                stacklevel=6
+            )
+            kwargs['minsupp_new'] = kwargs['min_rule_covered']
+        else:
+            kwargs['min_rule_covered'] = kwargs['minsupp_new']
+
+        if kwargs.get('induction_measure') == Measures.LogRank or \
+                kwargs.get('pruning_measure') == Measures.LogRank or \
+                kwargs.get('voting_measure') == Measures.LogRank:
+            self.LogRank = JClass('adaa.analytics.rules.logic.quality.LogRank')
+        for measure_param_name in ['induction_measure', 'pruning_measure', 'voting_measure']:
+            measure_param_value: Measures = kwargs.pop(
+                measure_param_name, None)
+            self._configure_measure_parameter(
+                measure_param_name, measure_param_value)
+        for param_name, param_value in kwargs.items():
+            self._configure_simple_parameter(param_name, param_value)
+
+
+def set_attributes_names(example_set, attributes_names: list[str]):
+    """Sets attributes names
+
+    Args:
+        example_set (_type_): ExampleSet
+        attributes_names (list[str]): attributes names
+    """
+    for index, name in enumerate(attributes_names):
+        example_set.getAttributes().get(f'att{index + 1}').setName(name)
+
+
+def set_attribute_role(example_set, attribute: str, role: str) -> object:
+    """Set attribute special role
+
+    Args:
+        example_set (_type_): ExampleSet
+        attribute (str): attribute name
+        role (str): attribute role
+
+    Returns:
+        object: _description_
+    """
+    OperatorDocumentation = JClass(  # pylint: disable=invalid-name
+        'com.rapidminer.tools.documentation.OperatorDocumentation'
+    )
+    OperatorDescription = JClass(  # pylint: disable=invalid-name
+        'com.rapidminer.operator.OperatorDescription'
+    )
+    Mockito = JClass('org.mockito.Mockito')  # pylint: disable=invalid-name
+    ChangeAttributeRole = JClass(  # pylint: disable=invalid-name
+        'com.rapidminer.operator.preprocessing.filter.ChangeAttributeRole'
+    )
+
+    documentation = Mockito.mock(OperatorDocumentation.class_)
+    description = Mockito.mock(OperatorDescription.class_)
+    Mockito.when(documentation.getShortName()).thenReturn(JString(''), None)
+    Mockito.when(description.getOperatorDocumentation()
+                 ).thenReturn(documentation, None)
+    role_setter = ChangeAttributeRole(description)
+    role_setter.setParameter(
+        ChangeAttributeRole.PARAMETER_NAME, attribute)
+    role_setter.setParameter(
+        ChangeAttributeRole.PARAMETER_TARGET_ROLE, role)
+    return role_setter.apply(example_set)
+
+
+def create_example_set(
+    values: Union[pd.DataFrame, np.ndarray],
+    labels: Union[pd.Series, np.ndarray] = None,
+    numeric_labels: bool = False,
+    survival_time_attribute: str = None,
+    contrast_attribute: str = None,
+) -> object:
+    """Creates Java RapidMiner ExampleSet object instance
+
+    Args:
+        values (Union[pd.DataFrame, np.ndarray]): Attributes values
+        labels (Union[pd.Series, np.ndarray], optional): Labels. Defaults to None.
+        numeric_labels (bool, optional): Whether labels are numerical or not . Defaults to False.
+        survival_time_attribute (str, optional): Name of special survival time attribute. Used 
+            for survival analysis Defaults to None.
+        contrast_attribute (str, optional): Name of special contrast attribute. Used 
+            for contrast sets analysis Defaults to None.
+
+    Returns:
+        object: ExampleSet object instance
+    """
+    if labels is None:
+        labels = ['' if not numeric_labels else 0] * len(values)
+    attributes_names = None
+    label_name = None
+    if isinstance(values, pd.DataFrame):
+        attributes_names = values.columns.values
+        values = values.to_numpy()
+    if isinstance(labels, pd.Series):
+        label_name = labels.name
+        labels = labels.to_numpy()
+    values = JObject(values, JArray('java.lang.Object', 2))
+    labels = JObject(labels, JArray('java.lang.Object', 1))
+    ExampleSetFactory = JClass(  # pylint: disable=invalid-name
+        'com.rapidminer.example.ExampleSetFactory'
+    )
+    example_set = ExampleSetFactory.createExampleSet(values, labels)
+    if attributes_names is not None:
+        set_attributes_names(example_set, attributes_names)
+    if label_name is not None:
+        example_set.getAttributes().get('label').setName(label_name)
+    if survival_time_attribute is not None:
+        if survival_time_attribute == '':
+            survival_time_attribute = f'att{example_set.getAttributes().size()}'
+        example_set = set_attribute_role(
+            example_set, survival_time_attribute, SURVIVAL_TIME_ATTR_ROLE)
+    if contrast_attribute is not None:
+        example_set = set_attribute_role(
+            example_set, contrast_attribute, CONTRAST_ATTR_ROLE)
+    return example_set
+
+
+class PredictionResultMapper:
+    """Maps prediction results to numpy array
+    """
+
+    @staticmethod
+    def map_confidence(
+        predicted_example_set,
+        label_unique_values: list
+    ) -> np.ndarray:
+        """Maps models confidence values to numpy array
+
+        Args:
+            predicted_example_set (_type_): predicted ExampleSet instance
+            label_unique_values (list): unique labels values
+
+        Returns:
+            np.ndarray: numpy array with mapped confidence values
+        """
+        confidence_attributes_names = list(
+            map(lambda val: f'confidence_{val}', label_unique_values))
+        prediction = []
+        row_reader = predicted_example_set.getExampleTable().getDataRowReader()
+        confidence_attributes = []
+        for name in confidence_attributes_names:
+            confidence_attributes.append(
+                predicted_example_set.getAttributes().get(name))
+        while row_reader.hasNext():
+            row = row_reader.next()
+            value = []
+            for attribute in confidence_attributes:
+                value.append(attribute.getValue(row))
+            prediction.append(np.array(value))
+        return np.array(prediction)
+
+    @staticmethod
+    def map(predicted_example_set) -> np.ndarray:
+        """Maps models predictions to numpy array
+
+        Args:
+            predicted_example_set (_type_): ExampleSet with predictions
+
+        Returns:
+            np.ndarray: numpy array containing predictions
+        """
+        attribute = predicted_example_set.getAttributes().get('prediction')
+        if attribute.isNominal():
+            return PredictionResultMapper.map_to_nominal(predicted_example_set)
+        else:
+            return PredictionResultMapper.map_to_numerical(predicted_example_set)
+
+    @staticmethod
+    def map_to_nominal(predicted_example_set) -> np.ndarray:
+        """Maps models predictions to nominal numpy array of strings
+
+        Args:
+            predicted_example_set (_type_): ExampleSet with predictions
+
+        Returns:
+            np.ndarray: numpy array containing predictions
+        """
+        prediction: list[str] = []
+        row_reader = predicted_example_set.getExampleTable().getDataRowReader()
+        attribute = predicted_example_set.getAttributes().get('prediction')
+        label_mapping = attribute.getMapping()
+        while row_reader.hasNext():
+            row = row_reader.next()
+            value_index = row.get(attribute)
+            value = label_mapping.mapIndex(round(value_index))
+            prediction.append(value)
+        prediction = [str(e) for e in prediction]
+        return np.array(prediction).astype(np.unicode_)
+
+    @staticmethod
+    def map_to_numerical(predicted_example_set, remap: bool = True) -> np.ndarray:
+        """Maps models predictions to numerical numpy array
+
+        Args:
+            predicted_example_set (_type_): ExampleSet with predictions
+
+        Returns:
+            np.ndarray: numpy array containing predictions
+        """
+        prediction = []
+        row_reader = predicted_example_set.getExampleTable().getDataRowReader()
+        attribute = predicted_example_set.getAttributes().get('prediction')
+        label_mapping = predicted_example_set.getAttributes().getLabel().getMapping()
+        while row_reader.hasNext():
+            row = row_reader.next()
+            if remap:
+                value = int(attribute.getValue(row))
+                value = float(str(label_mapping.mapIndex(value)))
+            else:
+                value = float(attribute.getValue(row))
+            prediction.append(value)
+        return np.array(prediction)
+
+    @staticmethod
+    def map_survival(predicted_example_set) -> np.ndarray:
+        """Maps survival models predictions to numpy array. Used as alternative to `map` method 
+        used in survival analysis
+
+        Args:
+            predicted_example_set (_type_): ExampleSet with predictions
+
+        Returns:
+            np.ndarray: numpy array containing predictions
+        """
+        estimators = []
+        attribute = predicted_example_set.getAttributes().get("estimator")
+        example_set_iterator = predicted_example_set.iterator()
+        while example_set_iterator.hasNext():
+            example = example_set_iterator.next()
+            example_estimator = str(example.getValueAsString(attribute))
+            example_estimator = example_estimator.split(" ")
+            _, example_estimator[0] = example_estimator[0].split(
+                ":")
+            times = [
+                float(example_estimator[i])
+                for i in range(len(example_estimator) - 1) if i % 2 == 0
+            ]
+            probabilities = [float(example_estimator[i])
+                             for i in range(len(example_estimator)) if i % 2 != 0]
+            estimator = {'times': times, 'probabilities': probabilities}
+            estimators.append(estimator)
+        return np.array(estimators)
+
+
+class ModelSerializer:
+    """Class for serializing models
+    """
+
+    @staticmethod
+    def serialize(real_model: object) -> bytes:
+        """Serialize Java ruleset object.
+
+        Args:
+            real_model (object): Java ruleset object
+        """
+        in_memory_file = io.BytesIO()
+        JPickler(in_memory_file).dump(real_model)
+        serialized_bytes = in_memory_file.getvalue()
+        in_memory_file.close()
+        return serialized_bytes
+
+    @staticmethod
+    def deserialize(serialized_bytes: bytes) -> object:
+        """Deserialize Java ruleset object from bytes.
+
+        Args:
+            serialized_bytes (bytes): serialized bytes
+
+        Returns:
+            object: deserialized Java ruleset object
+        """
+        in_memory_file = io.BytesIO(serialized_bytes)
+        model = JUnpickler(in_memory_file).load()
+        in_memory_file.close()
+        return model
diff --git a/rulekit/_operator.py b/rulekit/_operator.py
new file mode 100644
index 0000000..984be8a
--- /dev/null
+++ b/rulekit/_operator.py
@@ -0,0 +1,222 @@
+"""Contains base classes for rule induction operators
+"""
+from __future__ import annotations
+from typing import Union, Any
+import numpy as np
+import pandas as pd
+
+from ._helpers import (
+    RuleGeneratorConfigurator,
+    PredictionResultMapper,
+    create_example_set,
+    get_rule_generator,
+    ModelSerializer,
+)
+from .params import Measures, ModelsParams, ContrastSetModelParams
+from .rules import RuleSet, Rule
+
+
+Data = Union[np.ndarray, pd.DataFrame, list]
+
+DEFAULT_PARAMS_VALUE = {
+    'minsupp_new': 5,
+    'min_rule_covered': 5,
+    'induction_measure': Measures.Correlation,
+    'pruning_measure':  Measures.Correlation,
+    'voting_measure': Measures.Correlation,
+    'max_growing': 0.0,
+    'enable_pruning': True,
+    'ignore_missing': False,
+    'max_uncovered_fraction': 0.0,
+    'select_best_candidate': False,
+
+    'extend_using_preferred': None,
+    'extend_using_automatic': None,
+    'induce_using_preferred': None,
+    'induce_using_automatic': None,
+    'consider_other_classes': None,
+    'preferred_conditions_per_rule': None,
+    'preferred_attributes_per_rule': None,
+
+    # Contrast sets
+    'minsupp_all': (0.8, 0.5, 0.2, 0.1),
+    'max_neg2pos': 0.5,
+    'max_passes_count': 5,
+    'penalty_strength': 0.5,
+    'penalty_saturation': 0.2,
+}
+
+
+class BaseOperator:
+    """Base class for rule induction operator
+    """
+
+    def __init__(self, **kwargs):
+        if kwargs.get('minsupp_all', None) is not None and len(kwargs['minsupp_all']) > 0:
+            kwargs['minsupp_all'] = ' '.join(
+                [str(e) for e in kwargs['minsupp_all']]
+            )
+        self._params = None
+        self._rule_generator = None
+        self.set_params(**kwargs)
+        self.model: RuleSet = None
+
+    def _map_result(self, predicted_example_set) -> np.ndarray:
+        return PredictionResultMapper.map(predicted_example_set)
+
+    def fit(  # pylint: disable=missing-function-docstring
+        self,
+        values: Data,
+        labels: Data,
+        survival_time_attribute: str = None,
+        contrast_attribute: str = None,
+    ) -> BaseOperator:
+        example_set = create_example_set(
+            values,
+            labels,
+            survival_time_attribute=survival_time_attribute,
+            contrast_attribute=contrast_attribute
+        )
+        contrast_attribute_instance = example_set.getAttributes().get(contrast_attribute)
+        if contrast_attribute is not None and contrast_attribute_instance.isNumerical():
+            raise ValueError(
+                'Contrast set attributes must be a nominal attribute while ' +
+                f'"{contrast_attribute}" is a numerical one.'
+            )
+
+        java_model = self._rule_generator.learn(example_set)
+        self.model = RuleSet(java_model)
+        return self.model
+
+    def predict(self, values: Data) -> np.ndarray:  # pylint: disable=missing-function-docstring
+        if self.model is None:
+            raise ValueError(
+                '"fit" method must be called before calling this method')
+        example_set = create_example_set(values)
+        return self.model._java_object.apply(example_set)  # pylint: disable=protected-access
+
+    def get_params(self) -> dict[str, Any]:
+        """
+        Returns
+        -------
+        hyperparameters : np.ndarray
+            Dictionary containing model hyperparameters.
+        """
+        return self._params
+
+    def set_params(self, **kwargs) -> object:
+        """Set models hyperparameters. Parameters are the same as in constructor."""
+        self._rule_generator = get_rule_generator()
+        # validate
+        if 'minsupp_all' in kwargs:
+            ContrastSetModelParams(**kwargs)
+        else:
+            ModelsParams(**kwargs)
+        self._params = kwargs
+        configurator = RuleGeneratorConfigurator(self._rule_generator)
+        self._rule_generator = configurator.configure(**kwargs)
+        return self
+
+    def get_coverage_matrix(self, values: Data) -> np.ndarray:
+        """Calculates coverage matrix for ruleset.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            dataset
+
+        Returns
+        -------
+        coverage_matrix : np.ndarray
+            Each row of the matrix represent single example from dataset and every column represent
+            on rule from rule set. Value 1 in the matrix cell means that rule covered certain 
+            example, value 0 means that it doesn't.
+        """
+        if self.model is None:
+            raise ValueError(
+                '"fit" method must be called before calling this method')
+        covering_info = self.model.covering(create_example_set(values))
+        if isinstance(values, pd.Series) or isinstance(values, pd.DataFrame):
+            values = values.to_numpy()
+        result = []
+        for i in range(len(values)):
+            row_result: list[int] = []
+            for item in covering_info:
+                value = 0 if item is None or not i in item else 1
+                row_result.append(value)
+            result.append(np.array(row_result))
+        return np.array(result)
+
+    def __getstate__(self) -> dict:
+        state = self.__dict__.copy()
+        state.pop('_rule_generator')
+        return {
+            '_params': self._params,
+            'model': ModelSerializer.serialize(self.model)
+        }
+
+    def __setstate__(self, state: dict):
+        self.model = ModelSerializer.deserialize(state['model'])
+        self._rule_generator = get_rule_generator()
+        self.set_params(**state['_params'])
+
+
+class ExpertKnowledgeOperator:
+    """Base class for expert rule induction operator
+    """
+
+    def __init__(self, **kwargs):
+        self._params = None
+        self._rule_generator = None
+        self._configurator = None
+        ExpertKnowledgeOperator.set_params(self, **kwargs)
+        self.model: RuleSet = None
+
+    def fit(  # pylint: disable=missing-function-docstring
+        self,
+        values: Data,
+        labels: Data,
+        survival_time_attribute: str = None,
+        contrast_attribute: str = None,
+
+        expert_rules: list[Union[str, Rule]] = None,
+        expert_preferred_conditions: list[Union[str, Rule]] = None,
+        expert_forbidden_conditions: list[Union[str, Rule]] = None
+    ) -> ExpertKnowledgeOperator:
+        self._configurator._configure_simple_parameter(  # pylint: disable=protected-access
+            'use_expert', True)
+        self._configurator._configure_expert_parameter(  # pylint: disable=protected-access
+            'expert_preferred_conditions', expert_preferred_conditions
+        )
+        self._configurator._configure_expert_parameter(  # pylint: disable=protected-access
+            'expert_forbidden_conditions', expert_forbidden_conditions
+        )
+        self._configurator._configure_expert_parameter(  # pylint: disable=protected-access
+            'expert_rules', expert_rules
+        )
+        example_set = create_example_set(
+            values,
+            labels,
+            survival_time_attribute=survival_time_attribute,
+            contrast_attribute=contrast_attribute
+        )
+
+        java_model = self._rule_generator.learn(example_set)
+        self.model = RuleSet(java_model)
+        return self.model
+
+    def predict(self, values: Data) -> np.ndarray:  # pylint: disable=missing-function-docstring
+        if self.model is None:
+            raise ValueError(
+                '"fit" method must be called before calling this method')
+        example_set = create_example_set(values)
+        return self.model._java_object.apply(example_set)  # pylint: disable=protected-access
+
+    def set_params(self, **kwargs) -> object:  # pylint: disable=missing-function-docstring
+        # validate params
+        ModelsParams(**kwargs)
+        self._params = kwargs
+        self._rule_generator = get_rule_generator(expert=True)
+        self._configurator = RuleGeneratorConfigurator(self._rule_generator)
+        self._rule_generator = self._configurator.configure(**kwargs)
+        return self
diff --git a/rulekit/classification.py b/rulekit/classification.py
index 52c717b..b8a0a94 100644
--- a/rulekit/classification.py
+++ b/rulekit/classification.py
@@ -1,14 +1,22 @@
-from typing import Union, Any, List, Tuple, Dict
+"""Module contaiing classes for classification analysis and prediction.
+"""
+from __future__ import annotations
 from numbers import Number
+from typing import Union, Optional, Iterable
+from enum import Enum
 import numpy as np
 import pandas as pd
 from sklearn import metrics
-from enum import Enum
 from jpype import JClass
 
-from .helpers import PredictionResultMapper
+from ._helpers import PredictionResultMapper
 from .params import Measures
-from .operator import BaseOperator, ExpertKnowledgeOperator, Data, DEFAULT_PARAMS_VALUE
+from ._operator import (
+    Data,
+    BaseOperator,
+    ExpertKnowledgeOperator,
+    DEFAULT_PARAMS_VALUE
+)
 
 
 class BaseClassifier:
@@ -19,70 +27,94 @@ def __init__(self):
 
     class MetricTypes(Enum):
         """:meta private:"""
-        RulesPerExample = 1
-        VotingConflicts = 2
-        NegativeVotingConflicts = 3
+        RulesPerExample = 1  # pylint: disable=invalid-name
+        VotingConflicts = 2  # pylint: disable=invalid-name
+        NegativeVotingConflicts = 3  # pylint: disable=invalid-name
 
     def _init_classification_rule_performance(self):
-        self.ClassificationRulesPerformance = JClass(
-            'adaa.analytics.rules.logic.quality.ClassificationRulesPerformance')
+        self.ClassificationRulesPerformance = JClass(  # pylint: disable=invalid-name
+            'adaa.analytics.rules.logic.quality.ClassificationRulesPerformance'
+        )
 
     def _calculate_metric(self, example_set, metric_type) -> float:
-        classificationRulesPerformance = self.ClassificationRulesPerformance(
+        classification_rules_performance = self.ClassificationRulesPerformance(
             metric_type.value)
-        classificationRulesPerformance.startCounting(example_set, True)
-        return classificationRulesPerformance.getMikroAverage()
+        classification_rules_performance.startCounting(example_set, True)
+        return classification_rules_performance.getMikroAverage()
 
-    def _calculate_prediction_metrics(self, example_set) -> Dict[str, float]:
+    def _calculate_prediction_metrics(self, example_set) -> dict[str, float]:
         return {
-            'rules_per_example': self._calculate_metric(example_set, BaseClassifier.MetricTypes.RulesPerExample),
-            'voting_conflicts': self._calculate_metric(example_set, BaseClassifier.MetricTypes.VotingConflicts),
-            'negative_voting_conflicts': self._calculate_metric(example_set, BaseClassifier.MetricTypes.NegativeVotingConflicts),
+            'rules_per_example': self._calculate_metric(
+                example_set, BaseClassifier.MetricTypes.RulesPerExample
+            ),
+            'voting_conflicts': self._calculate_metric(
+                example_set, BaseClassifier.MetricTypes.VotingConflicts
+            ),
+            'negative_voting_conflicts': self._calculate_metric(
+                example_set, BaseClassifier.MetricTypes.NegativeVotingConflicts
+            ),
         }
 
 
 class RuleClassifier(BaseOperator, BaseClassifier):
     """Classification model."""
 
-    def __init__(self,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                 pruning_measure: Union[Measures,
-                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']):
+    def __init__(
+        self,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
+        pruning_measure: Union[Measures,
+                               str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
+        voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
+        max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+        min_rule_covered: Optional[int] = None,
+    ):
         """
         Parameters
         ----------
-        min_rule_covered : int = 5
-            positive integer representing minimum number of previously uncovered examples to be covered by a new rule 
-            (positive examples for classification problems); default: 5
-        induction_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be
+            covered by a new rule (positive examples for classification problems); default: 5
+        induction_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.\
+            Measures.Correlation`
             measure used during induction; default measure is correlation
-        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = :class:`rulekit.params.Measures.Correlation`
-            measure used during pruning. Could be user defined (string), for example  :code:`2 * p / n`; 
-            default measure is correlation
-        voting_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during pruning. Could be user defined (string), for example  
+            :code:`2 * p / n`; default measure is correlation
+        voting_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during voting; default measure is correlation
         max_growing : int = 0.0
-            non-negative integer representing maximum number of conditions which can be added to the rule in the growing phase 
-            (use this parameter for large datasets if execution time is prohibitive); 0 indicates no limit; default: 0,
+            non-negative integer representing maximum number of conditions which can be added
+            to the rule in the growing phase (use this parameter for large datasets if execution
+            time is prohibitive); 0 indicates no limit; default: 0,
         enable_pruning : bool = True
             enable or disable pruning, default is True.
         ignore_missing : bool = False
-            boolean telling whether missing values should be ignored (by default, a missing value of given attribute is always 
-            considered as not fulfilling the condition build upon that attribute); default: False.
+            boolean telling whether missing values should be ignored (by default, a missing value
+            of given attribute is always cconsidered as not fulfilling the condition build upon 
+            that attribute); default: False.
         max_uncovered_fraction : float = 0.0
-            Floating-point number from [0,1] interval representing maximum fraction of examples that may remain uncovered by the rule set, default: 0.0.
+            Floating-point number from [0,1] interval representing maximum fraction of examples
+            that may remain uncovered by the rule set, default: 0.0.
         select_best_candidate : bool = False
-            Flag determining if best candidate should be selected from growing phase; default: False.
+            Flag determining if best candidate should be selected from growing phase; default:
+            False.
+        min_rule_covered : int = None
+            alias to `minsupp_new`. Parameter is deprecated and will be removed in the next major
+            version, use `minsupp_new`
+
+            .. deprecated:: 1.7.0
+                Use parameter `minsupp_new` instead.
         """
         BaseOperator.__init__(
             self,
+            minsupp_new=minsupp_new,
             min_rule_covered=min_rule_covered,
             induction_measure=induction_measure,
             pruning_measure=pruning_measure,
@@ -91,7 +123,8 @@ def __init__(self,
             enable_pruning=enable_pruning,
             ignore_missing=ignore_missing,
             max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate)
+            select_best_candidate=select_best_candidate
+        )
         BaseClassifier.__init__(self)
         self._remap_to_numeric = False
         self.label_unique_values = []
@@ -107,7 +140,9 @@ def _map_result(self, predicted_example_set) -> np.ndarray:
         return prediction
 
     def _map_confidence(self, predicted_example_set) -> np.ndarray:
-        return PredictionResultMapper.map_confidence(predicted_example_set, self.label_unique_values)
+        return PredictionResultMapper.map_confidence(
+            predicted_example_set, self.label_unique_values
+        )
 
     def _get_unique_label_values(self, labels: Data):
         tmp = {}
@@ -118,7 +153,18 @@ def _get_unique_label_values(self, labels: Data):
             self.label_unique_values = [item.decode(
                 'utf-8') for item in self.label_unique_values]
 
-    def fit(self, values: Data, labels: Data) -> Any:
+    def _prepare_labels(self, labels: Data) -> Data:
+        if isinstance(labels, pd.DataFrame) or isinstance(labels, pd.Series):
+            if isinstance(labels.iloc[0], Number):
+                self._remap_to_numeric = True
+                return labels.astype(str)
+        else:
+            if isinstance(labels[0], Number):
+                self._remap_to_numeric = True
+                return list(map(str, labels))
+        return labels
+
+    def fit(self, values: Data, labels: Data) -> RuleClassifier:  # pylint: disable=arguments-differ
         """Train model on given dataset.
 
         Parameters
@@ -132,19 +178,15 @@ def fit(self, values: Data, labels: Data) -> Any:
         self : RuleClassifier
         """
         self._get_unique_label_values(labels)
-
-        if isinstance(labels, pd.DataFrame) or isinstance(labels, pd.Series):
-            if isinstance(labels.iloc[0], Number):
-                self._remap_to_numeric = True
-                labels = labels.astype(str)
-        else:
-            if isinstance(labels[0], Number):
-                self._remap_to_numeric = True
-                labels = list(map(str, labels))
+        labels = self._prepare_labels(labels)
         BaseOperator.fit(self, values, labels)
         return self
 
-    def predict(self, values: Data, return_metrics: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]:
+    def predict(
+        self,
+        values: Data,
+        return_metrics: bool = False
+    ) -> Union[np.ndarray, tuple[np.ndarray, dict[str, float]]]:
         """Perform prediction and returns predicted labels.
 
         Parameters
@@ -159,19 +201,23 @@ def predict(self, values: Data, return_metrics: bool = False) -> Union[np.ndarra
         Returns
         -------
         result : Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]
-            If *return_metrics* flag wasn't set it will return just prediction, otherwise a tuple will be returned with first
-            element being prediction and second one being metrics.
+            If *return_metrics* flag wasn't set it will return just prediction, otherwise a tuple 
+            will be returned with first element being prediction and second one being metrics.
         """
         result_example_set = BaseOperator.predict(self, values)
         mapped_result_example_set = self._map_result(result_example_set)
         if return_metrics:
-            metrics = BaseClassifier._calculate_prediction_metrics(
+            metrics_values: dict = BaseClassifier._calculate_prediction_metrics(
                 self, result_example_set)
-            return (mapped_result_example_set, metrics)
+            return (mapped_result_example_set, metrics_values)
         else:
             return mapped_result_example_set
 
-    def predict_proba(self, values: Data, return_metrics: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]:
+    def predict_proba(
+        self,
+        values: Data,
+        return_metrics: bool = False
+    ) -> Union[np.ndarray, tuple[np.ndarray, dict[str, float]]]:
         """Perform prediction and returns class probabilities for each example.
 
         Parameters
@@ -186,15 +232,16 @@ def predict_proba(self, values: Data, return_metrics: bool = False) -> Union[np.
         Returns
         -------
         result : Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]
-            If *return_metrics* flag wasn't set it will return just probabilities matrix, otherwise a tuple will be returned with first
-            element being prediction and second one being metrics.
+            If *return_metrics* flag wasn't set it will return just probabilities matrix, otherwise
+            a tuple will be returned with first element being prediction and second one being 
+            metrics.
         """
         result_example_set = BaseOperator.predict(self, values)
         mapped_result_example_set = self._map_confidence(result_example_set)
         if return_metrics:
-            metrics = BaseClassifier._calculate_prediction_metrics(
+            metrics_values: dict = BaseClassifier._calculate_prediction_metrics(
                 self, result_example_set)
-            return (mapped_result_example_set, metrics)
+            return (mapped_result_example_set, metrics_values)
         else:
             return mapped_result_example_set
 
@@ -229,73 +276,108 @@ def __setstate__(self, state: dict):
         self._remap_to_numeric = state['_remap_to_numeric']
 
 
-class ExpertRuleClassifier(ExpertKnowledgeOperator, RuleClassifier, BaseClassifier):
+class ExpertRuleClassifier(ExpertKnowledgeOperator, RuleClassifier):
     """Classification model using expert knowledge."""
 
-    def __init__(self,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                 pruning_measure: Union[Measures,
-                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
-
-                 extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
-                 extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
-                 induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
-                 induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
-                 consider_other_classes: bool = DEFAULT_PARAMS_VALUE['consider_other_classes'],
-                 preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
-                     'preferred_conditions_per_rule'],
-                 preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE['preferred_attributes_per_rule']):
+    def __init__(
+        self,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
+        pruning_measure: Union[Measures,
+                               str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
+        voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
+        max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+
+        extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
+        extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
+        induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
+        induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
+        consider_other_classes: bool = DEFAULT_PARAMS_VALUE['consider_other_classes'],
+        preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
+            'preferred_conditions_per_rule'],
+        preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE[
+            'preferred_attributes_per_rule'],
+        min_rule_covered: Optional[int] = None
+    ):
         """
         Parameters
         ----------
-        min_rule_covered : int = 5
-            positive integer representing minimum number of previously uncovered examples to be covered by a new rule 
-            (positive examples for classification problems); default: 5
-        induction_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be
+            covered by a new rule (positive examples for classification problems); default: 5
+        induction_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during induction; default measure is correlation
-        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = :class:`rulekit.params.Measures.Correlation`
-            measure used during pruning. Could be user defined (string), for example  :code:`2 * p / n`; 
-            default measure is correlation
-        voting_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during pruning. Could be user defined (string), for example  
+            :code:`2 * p / n`; default measure is correlation
+        voting_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during voting; default measure is correlation
         max_growing : int = 0.0
-            non-negative integer representing maximum number of conditions which can be added to the rule in the growing phase 
-            (use this parameter for large datasets if execution time is prohibitive); 0 indicates no limit; default: 0,
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
         enable_pruning : bool = True
             enable or disable pruning, default is True.
         ignore_missing : bool = False
-            boolean telling whether missing values should be ignored (by default, a missing value of given attribute is always 
-            considered as not fulfilling the condition build upon that attribute); default: False.
+            boolean telling whether missing values should be ignored (by default, a missing value
+            of given attribute is always considered as not fulfilling the condition build upon 
+            that attribute); default: False.
         max_uncovered_fraction : float = 0.0
-            Floating-point number from [0,1] interval representing maximum fraction of examples that may remain uncovered by the rule set, default: 0.0.
+            Floating-point number from [0,1] interval representing maximum fraction of examples
+            that may remain uncovered by the rule set, default: 0.0.
         select_best_candidate : bool = False
-            Flag determining if best candidate should be selected from growing phase; default: False.
-
+            Flag determining if best candidate should be selected from growing phase; default: 
+            False.
         extend_using_preferred : bool = False
-            boolean indicating whether initial rules should be extended with a use of preferred conditions and attributes; default is False
+            boolean indicating whether initial rules should be extended with a use of preferred
+            conditions and attributes; default is False
         extend_using_automatic : bool = False
-            boolean indicating whether initial rules should be extended with a use of automatic conditions and attributes; default is False
+            boolean indicating whether initial rules should be extended with a use of automatic 
+            conditions and attributes; default is False
         induce_using_preferred : bool = False
-            boolean indicating whether new rules should be induced with a use of preferred conditions and attributes; default is False
+            boolean indicating whether new rules should be induced with a use of preferred 
+            conditions and attributes; default is False
         induce_using_automatic : bool = False
-            boolean indicating whether new rules should be induced with a use of automatic conditions and attributes; default is False
+            boolean indicating whether new rules should be induced with a use of automatic 
+            conditions and attributes; default is False
         consider_other_classes : bool = False
-            boolean indicating whether automatic induction should be performed for classes for which no user's knowledge has been defined (classification only); default is False.
+            boolean indicating whether automatic induction should be performed for classes for
+              which no user's knowledge has been defined (classification only); default is False.
         preferred_conditions_per_rule : int = None
             maximum number of preferred conditions per rule; default: unlimited,
         preferred_attributes_per_rule : int = None
             maximum number of preferred attributes per rule; default: unlimited.
+        min_rule_covered : int = None
+            alias to `minsupp_new`. Parameter is deprecated and will be removed in the next major
+            version, use `minsupp_new`
+
+            .. deprecated:: 1.7.0
+                Use parameter `minsupp_new` instead.
         """
         self._remap_to_numeric = False
+        RuleClassifier.__init__(
+            self,
+            minsupp_new=minsupp_new,
+            min_rule_covered=min_rule_covered,
+            induction_measure=induction_measure,
+            pruning_measure=pruning_measure,
+            voting_measure=voting_measure,
+            max_growing=max_growing,
+            enable_pruning=enable_pruning,
+            ignore_missing=ignore_missing,
+            max_uncovered_fraction=max_uncovered_fraction,
+            select_best_candidate=select_best_candidate,
+        )
         ExpertKnowledgeOperator.__init__(
             self,
+            minsupp_new=minsupp_new,
             min_rule_covered=min_rule_covered,
             induction_measure=induction_measure,
             pruning_measure=pruning_measure,
@@ -314,14 +396,15 @@ def __init__(self,
             preferred_attributes_per_rule=preferred_attributes_per_rule
         )
 
-    def fit(self,
-            values: Data,
-            labels: Data,
+    def fit(  # pylint: disable=arguments-differ
+        self,
+        values: Data,
+        labels: Data,
 
-            expert_rules: List[Union[str, Tuple[str, str]]] = None,
-            expert_preferred_conditions: List[Union[str,
-                                                    Tuple[str, str]]] = None,
-            expert_forbidden_conditions: List[Union[str, Tuple[str, str]]] = None) -> Any:
+        expert_rules: list[Union[str, tuple[str, str]]] = None,
+        expert_preferred_conditions: list[Union[str, tuple[str, str]]] = None,
+        expert_forbidden_conditions: list[Union[str, tuple[str, str]]] = None
+    ) -> ExpertRuleClassifier:
         """Train model on given dataset.
 
         Parameters
@@ -332,14 +415,16 @@ def fit(self,
             labels
 
         expert_rules : List[Union[str, Tuple[str, str]]]
-             set of initial rules, either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            set of initial rules, either passed as a list of strings representing rules or as list
+            of tuples where first element is name of the rule and second one is rule string.
         expert_preferred_conditions : List[Union[str, Tuple[str, str]]]
-             multiset of preferred conditions (used also for specifying preferred attributes by using special value Any). Either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            multiset of preferred conditions (used also for specifying preferred attributes by
+            using special value Any). Either passed as a list of strings representing rules or as
+            list of tuples where first element is name of the rule and second one is rule string.
         expert_forbidden_conditions : List[Union[str, Tuple[str, str]]]
-             set of forbidden conditions (used also for specifying forbidden attributes by using special valye Any). Either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            set of forbidden conditions (used also for specifying forbidden attributes by using 
+            special valye Any). Either passed as a list of strings representing rules or as list
+            of tuples where first element is name of the rule and second one is rule string.
         Returns
         -------
         self : ExpertRuleClassifier
@@ -361,7 +446,11 @@ def fit(self,
             expert_forbidden_conditions=expert_forbidden_conditions
         )
 
-    def predict(self, values: Data, return_metrics: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]:
+    def predict(
+        self,
+        values: Data,
+        return_metrics: bool = False
+    ) -> Union[np.ndarray, tuple[np.ndarray, dict[str, float]]]:
         return RuleClassifier.predict(self, values, return_metrics)
 
     def __getstate__(self) -> dict:
@@ -372,3 +461,218 @@ def __getstate__(self) -> dict:
     def __setstate__(self, state: dict):
         BaseOperator.__setstate__(self, state)
         self._remap_to_numeric = state['_remap_to_numeric']
+
+
+class ContrastSetRuleClassifier(BaseOperator, BaseClassifier):
+    """Contrast set classification model."""
+
+    def __init__(
+        self,
+        minsupp_all: Iterable[float] = DEFAULT_PARAMS_VALUE['minsupp_all'],
+        max_neg2pos: float = DEFAULT_PARAMS_VALUE['max_neg2pos'],
+        max_passes_count: int = DEFAULT_PARAMS_VALUE['max_passes_count'],
+        penalty_strength: float = DEFAULT_PARAMS_VALUE['penalty_strength'],
+        penalty_saturation: float = DEFAULT_PARAMS_VALUE['penalty_saturation'],
+
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
+        pruning_measure: Union[Measures,
+                               str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
+        voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
+        max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+    ):
+        """
+        Parameters
+        ----------
+        minsupp_all: Iterable[float]
+            a minimum positive support of a contrast set (p/P). When multiple values are specified,
+            a metainduction is performed; Default and recommended sequence is: 0.8, 0.5, 0.2, 0.1
+        max_neg2pos: float
+            a maximum ratio of negative to positive supports (nP/pN); Default is 0.5
+        max_passes_count: int
+            a maximum number of sequential covering passes for a single minsupp-all; Default is 5
+        penalty_strength: float
+            (s) - penalty strength; Default is 0.5
+        penalty_saturation: float
+            the value of p_new / P at which penalty reward saturates; Default is 0.2.
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be 
+            covered by a new rule (positive examples for classification problems); default: 5
+        induction_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during induction; default measure is correlation
+        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during pruning. Could be user defined (string), for example  
+            :code:`2 * p / n`; default measure is correlation
+        voting_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during voting; default measure is correlation
+        max_growing : int = 0.0
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
+        enable_pruning : bool = True
+            enable or disable pruning, default is True.
+        ignore_missing : bool = False
+            boolean telling whether missing values should be ignored (by default, a missing value
+            of given attribute is always considered as not fulfilling the condition build upon 
+            that attribute); default: False.
+        max_uncovered_fraction : float = 0.0
+            Floating-point number from [0,1] interval representing maximum fraction of examples 
+            that may remain uncovered by the rule set, default: 0.0.
+        select_best_candidate : bool = False
+            Flag determining if best candidate should be selected from growing phase; 
+            default: False.
+        """
+        BaseOperator.__init__(
+            self,
+            minsupp_all=minsupp_all,
+            max_neg2pos=max_neg2pos,
+            max_passes_count=max_passes_count,
+            penalty_strength=penalty_strength,
+            penalty_saturation=penalty_saturation,
+            minsupp_new=minsupp_new,
+            induction_measure=induction_measure,
+            pruning_measure=pruning_measure,
+            voting_measure=voting_measure,
+            max_growing=max_growing,
+            enable_pruning=enable_pruning,
+            ignore_missing=ignore_missing,
+            max_uncovered_fraction=max_uncovered_fraction,
+            select_best_candidate=select_best_candidate)
+        BaseClassifier.__init__(self)
+        self.contrast_attribute: str = None
+        self._remap_to_numeric = False
+        self.label_unique_values = []
+
+    def _map_result(self, predicted_example_set) -> np.ndarray:
+        prediction: np.ndarray
+        if self._remap_to_numeric:
+            prediction = PredictionResultMapper.map_to_numerical(
+                predicted_example_set)
+        else:
+            prediction = PredictionResultMapper.map_to_nominal(
+                predicted_example_set)
+        return prediction
+
+    def _get_unique_label_values(self, labels: Data):
+        tmp = {}
+        for label_value in labels:
+            tmp[label_value] = None
+        self.label_unique_values = list(tmp.keys())
+        if len(self.label_unique_values) > 0 and isinstance(self.label_unique_values[0], bytes):
+            self.label_unique_values = [item.decode(
+                'utf-8') for item in self.label_unique_values]
+
+    def fit(self, values: Data, labels: Data, contrast_attribute: str) -> ContrastSetRuleClassifier:  # pylint: disable=arguments-differ
+        """Train model on given dataset.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+        labels : :class:`rulekit.operator.Data`
+            labels
+        contrast_attribute: str 
+            group attribute
+        Returns
+        -------
+        self : ContrastSetRuleClassifier
+        """
+        RuleClassifier._get_unique_label_values(  # pylint: disable=protected-access
+            self, labels
+        )
+        RuleClassifier._prepare_labels(  # pylint: disable=protected-access,protected-access
+            self, labels
+        )
+        BaseOperator.fit(
+            self, values, labels,
+            contrast_attribute=contrast_attribute
+        )
+        self.contrast_attribute = contrast_attribute
+        return self
+
+    def predict(
+        self,
+        values: Data,
+        return_metrics: bool = False
+    ) -> Union[np.ndarray, tuple[np.ndarray, dict[str, float]]]:
+        """Perform prediction and returns predicted labels.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+
+        return_metrics: bool = False
+            Optional flag. If set to *True* method will calculate some additional model metrics. 
+            Method will then return tuple instead of just predicted labels.
+
+        Returns
+        -------
+        result : Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]
+            If *return_metrics* flag wasn't set it will return just prediction, otherwise a tuple
+            will be returned with first element being prediction and second one being metrics.
+        """
+        return RuleClassifier.predict(self, values, return_metrics)
+
+    def predict_proba(
+        self,
+        values: Data,
+        return_metrics: bool = False
+    ) -> Union[np.ndarray, tuple[np.ndarray, dict[str, float]]]:
+        """Perform prediction and returns class probabilities for each example.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+
+        return_metrics: bool = False
+            Optional flag. If set to *True* method will calculate some additional model metrics. 
+            Method will then return tuple instead of just probabilities.
+
+        Returns
+        -------
+        result : Union[np.ndarray, Tuple[np.ndarray, Dict[str, float]]]
+            If *return_metrics* flag wasn't set it will return just probabilities matrix, otherwise
+            a tuple will be returned with first element being prediction and second one being 
+            metrics.
+        """
+        return RuleClassifier.predict_proba(self, values, return_metrics)
+
+    def score(self, values: Data, labels: Data) -> float:
+        """Return the accuracy on the given test data and labels.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+        labels : :class:`rulekit.operator.Data`
+            true labels
+
+        Returns
+        -------
+        score : float
+            Accuracy of self.predict(values) wrt. labels.
+        """
+        return RuleClassifier.score(self, values, labels)
+
+    def __getstate__(self) -> dict:
+        return {**BaseOperator.__getstate__(self), **{
+            'label_unique_values': self.label_unique_values,
+            '_remap_to_numeric': self._remap_to_numeric,
+            'contrast_attribute': self.contrast_attribute,
+        }}
+
+    def __setstate__(self, state: dict):
+        BaseOperator.__setstate__(self, state)
+        self._init_classification_rule_performance()
+        self.label_unique_values = state['label_unique_values']
+        self._remap_to_numeric = state['_remap_to_numeric']
+        self.contrast_attribute = state['contrast_attribute']
diff --git a/rulekit/experiment.py b/rulekit/experiment.py
deleted file mode 100644
index 96e1cad..0000000
--- a/rulekit/experiment.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from jpype import JClass
-from typing import List
-
-
-class ExperimentRunner:
-
-    @staticmethod
-    def run(args: List[str]):
-        ExperimentalConsole = JClass('adaa.analytics.rules.consoles.ExperimentalConsole')
-        ExperimentalConsole.main(args)
diff --git a/rulekit/main.py b/rulekit/main.py
index 4016d85..d9bfb7d 100644
--- a/rulekit/main.py
+++ b/rulekit/main.py
@@ -1,26 +1,25 @@
+"""Contains classes for initializing RuleKit Java backend
+"""
 import os
-import sys
-import jpype
-import jpype.imports
-
-from typing import List
 import glob
 import logging
 import zipfile
 import re
 from enum import Enum
-from subprocess import Popen,PIPE,STDOUT
+from subprocess import Popen, PIPE, STDOUT
+import jpype
+import jpype.imports
 
 
-class JRE_Type(Enum):
+class JRE_Type(Enum): # pylint: disable=invalid-name
     """:meta private:"""
-    open_jdk = 'open_jdk'
-    oracle = 'oracle'
+    OPEN_JDK = 'open_jdk'
+    ORACLE = 'oracle'
 
 
 class RuleKit:
     """Class used for initializing RuleKit
-    
+
     It starts JVM underhood and setups it with jars. 
 
     Attributes
@@ -34,20 +33,21 @@ class RuleKit:
     _class_path: str
     _rulekit_jar_file_path: str
     _jre_type: JRE_Type
+    initialized: bool = False
 
     @staticmethod
     def _detect_jre_type():
         try:
-            output = Popen(["java", "-version"],stderr=STDOUT,stdout=PIPE)
-            exit_code = output.returncode
+            output = Popen(["java", "-version"], stderr=STDOUT, stdout=PIPE)
             output = str(output.communicate()[0])
             if 'openjdk' in output:
-                RuleKit._jre_type = JRE_Type.open_jdk
+                RuleKit._jre_type = JRE_Type.OPEN_JDK
             else:
-                RuleKit._jre_type = JRE_Type.oracle
+                RuleKit._jre_type = JRE_Type.ORACLE
         except FileNotFoundError as error:
-            raise Exception('RuletKit requires java JRE to be installed (version 1.8.0 recommended)')
-
+            raise RuntimeError(
+                'RuletKit requires java JRE to be installed (version 1.8.0 recommended)'
+            ) from error
 
     @staticmethod
     def init(initial_heap_size: int = None, max_heap_size: int = None):
@@ -68,6 +68,8 @@ def init(initial_heap_size: int = None, max_heap_size: int = None):
         Exception
             If failed to load RuleKit jar file.
         """
+        if RuleKit.initialized:
+            return
         RuleKit._setup_logger()
 
         RuleKit._detect_jre_type()
@@ -75,19 +77,21 @@ def init(initial_heap_size: int = None, max_heap_size: int = None):
         RuleKit._jar_dir_path = f"{current_path}/jar"
         class_path_separator = os.pathsep
         try:
-            jars_paths: List[str] = glob.glob(f"{RuleKit._jar_dir_path}/*.jar")
+            jars_paths: list[str] = glob.glob(f"{RuleKit._jar_dir_path}/*.jar")
             RuleKit._class_path = f'{str.join(class_path_separator, jars_paths)}'
-            RuleKit._rulekit_jar_file_path = list(filter(lambda path: 'rulekit' in os.path.basename(path), jars_paths))[0]
+            RuleKit._rulekit_jar_file_path = list(
+                filter(lambda path: 'rulekit' in os.path.basename(path), jars_paths))[0]
         except IndexError as error:
             RuleKit._logger.error('Failed to load jar files')
-            raise Exception('''\n
+            raise RuntimeError('''\n
 Failed to load RuleKit jar file. Check if valid rulekit jar file is present in "rulekit/jar" directory.
 
 If you're running this packae for the first time you need to download RuleKit jar file by running:
     python -m rulekit download_jar
-        ''')
+        ''') from error
         RuleKit._read_versions()
         RuleKit._launch_jvm(initial_heap_size, max_heap_size)
+        RuleKit.initialized = True
 
     @staticmethod
     def _setup_logger():
@@ -98,10 +102,13 @@ def _setup_logger():
     def _read_versions():
         jar_archive = zipfile.ZipFile(RuleKit._rulekit_jar_file_path, 'r')
         try:
-            manifest_file_content: str = jar_archive.read('META-INF/MANIFEST.MF').decode('utf-8')
-            RuleKit.version = re.findall(r'Implementation-Version: \S+\r', manifest_file_content)[0].split(' ')[1]
+            manifest_file_content: str = jar_archive.read(
+                'META-INF/MANIFEST.MF').decode('utf-8')
+            RuleKit.version = re.findall(
+                r'Implementation-Version: \S+\r', manifest_file_content)[0].split(' ')[1]
         except Exception as error:
-            RuleKit._logger.error('Failed to read RuleKit versions from jar file')
+            RuleKit._logger.error(
+                'Failed to read RuleKit versions from jar file')
             RuleKit._logger.error(error)
             raise error
 
@@ -117,4 +124,5 @@ def _launch_jvm(initial_heap_size: int, max_heap_size: int):
                 params.append(f'-Xms{initial_heap_size}m')
             if max_heap_size is not None:
                 params.append(f'-Xmx{max_heap_size}m')
-            jpype.startJVM(jpype.getDefaultJVMPath(), *params, convertStrings=False)
+            jpype.startJVM(jpype.getDefaultJVMPath(), *
+                           params, convertStrings=False)
diff --git a/rulekit/operator.py b/rulekit/operator.py
deleted file mode 100644
index 69942fb..0000000
--- a/rulekit/operator.py
+++ /dev/null
@@ -1,279 +0,0 @@
-from .helpers import RuleGeneratorConfigurator, \
-    PredictionResultMapper, \
-    create_example_set, \
-    get_rule_generator, \
-    ModelSerializer
-from .params import Measures
-from .rules import RuleSet, Rule
-import numpy as np
-import pandas as pd
-from typing import Union, Any, List
-
-Data = Union[np.ndarray, pd.DataFrame, List]
-
-DEFAULT_PARAMS_VALUE = {
-    'min_rule_covered': 5,
-    'induction_measure': Measures.Correlation,
-    'pruning_measure':  Measures.Correlation,
-    'voting_measure': Measures.Correlation,
-    'max_growing': 0.0,
-    'enable_pruning': True,
-    'ignore_missing': False,
-    'max_uncovered_fraction': 0.0,
-    'select_best_candidate': False,
-
-    'extend_using_preferred': None,
-    'extend_using_automatic': None,
-    'induce_using_preferred': None,
-    'induce_using_automatic': None,
-    'consider_other_classes': None,
-    'preferred_conditions_per_rule': None,
-    'preferred_attributes_per_rule': None,
-}
-
-
-class BaseOperator:
-
-    def __init__(self,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                 pruning_measure: Union[Measures,
-                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']):
-        self._params = None
-        self._rule_generator = None
-        self.set_params(
-            min_rule_covered=min_rule_covered,
-            induction_measure=induction_measure,
-            pruning_measure=pruning_measure,
-            voting_measure=voting_measure,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate
-        )
-        self.model: RuleSet = None
-
-    def _map_result(self, predicted_example_set) -> np.ndarray:
-        return PredictionResultMapper.map(predicted_example_set)
-
-    def fit(self, values: Data, labels: Data, survival_time_attribute: str = None) -> Any:
-        example_set = create_example_set(
-            values, labels, survival_time_attribute=survival_time_attribute)
-        java_model = self._rule_generator.learn(example_set)
-        self.model = RuleSet(java_model)
-        return self.model
-
-    def predict(self, values: Data) -> np.ndarray:
-        if self.model is None:
-            raise ValueError(
-                '"fit" method must be called before calling this method')
-        example_set = create_example_set(values)
-        return self.model._java_object.apply(example_set)
-
-    def get_params(self, deep=True) -> dict:
-        """
-        Returns
-        -------
-        hyperparameters : np.ndarray
-            Dictionary containing model hyperparameters.
-        """
-        return self._params
-
-    def set_params(self,
-                   min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                   induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                   pruning_measure: Union[Measures,
-                                          str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                   voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                   max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                   enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                   ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                   max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                   select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']) -> object:
-        """Set models hyperparameters. Parameters are the same as in constructor."""
-        self._rule_generator = get_rule_generator()
-        self._params = dict(
-            min_rule_covered=min_rule_covered,
-            induction_measure=induction_measure,
-            pruning_measure=pruning_measure,
-            voting_measure=voting_measure,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate
-        )
-        configurator = RuleGeneratorConfigurator(self._rule_generator)
-        self._rule_generator = configurator.configure(**self._params)
-        return self
-
-    def get_coverage_matrix(self, values: Data) -> np.ndarray:
-        """Calculates coverage matrix for ruleset.
-
-        Parameters
-        ----------
-        values : :class:`rulekit.operator.Data`
-            dataset
-
-        Returns
-        -------
-        coverage_matrix : np.ndarray
-             Each row of the matrix represent single example from dataset and every column represent
-            on rule from rule set. Value 1 in the matrix cell means that rule covered certain example, value 0
-            means that it doesn't.
-        """
-        if self.model is None:
-            raise ValueError(
-                '"fit" method must be called before calling this method')
-        covering_info = self.model.covering(create_example_set(values))
-        if isinstance(values, pd.Series) or isinstance(values, pd.DataFrame):
-            values = values.to_numpy()
-        result = []
-        i = 0
-        for row in values:
-            row_result = []
-            for item in covering_info:
-                value = 0 if item is None or not i in item else 1
-                row_result.append(value)
-            result.append(np.array(row_result))
-            i += 1
-        return np.array(result)
-
-    def __getstate__(self) -> dict:
-        state = self.__dict__.copy()
-        state.pop('_rule_generator')
-        return {
-            '_params': self._params,
-            'model': ModelSerializer.serialize(self.model)
-        }
-
-    def __setstate__(self, state: dict):
-        self.model = ModelSerializer.deserialize(state['model'])
-        self._rule_generator = get_rule_generator()
-        self.set_params(**state['_params'])
-
-
-class ExpertKnowledgeOperator:
-
-    def __init__(self,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                 pruning_measure: Union[Measures,
-                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
-
-                 extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
-                 extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
-                 induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
-                 induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
-                 consider_other_classes: bool = DEFAULT_PARAMS_VALUE['consider_other_classes'],
-                 preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
-                     'preferred_conditions_per_rule'],
-                 preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE['preferred_attributes_per_rule']):
-        self._params = None
-        self._rule_generator = None
-        self._configurator = None
-        ExpertKnowledgeOperator.set_params(
-            self,
-            min_rule_covered=min_rule_covered,
-            induction_measure=induction_measure,
-            pruning_measure=pruning_measure,
-            voting_measure=voting_measure,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate,
-            extend_using_preferred=extend_using_preferred,
-            extend_using_automatic=extend_using_automatic,
-            induce_using_preferred=induce_using_preferred,
-            induce_using_automatic=induce_using_automatic,
-            consider_other_classes=consider_other_classes,
-            preferred_conditions_per_rule=preferred_conditions_per_rule,
-            preferred_attributes_per_rule=preferred_attributes_per_rule
-        )
-        self.model: RuleSet = None
-
-    def fit(self,
-            values: Data,
-            labels: Data,
-            survival_time_attribute: str = None,
-
-            expert_rules: List[Union[str, Rule]] = None,
-            expert_preferred_conditions: List[Union[str, Rule]] = None,
-            expert_forbidden_conditions: List[Union[str, Rule]] = None) -> Any:
-        self._configurator.configure_simple_parameter('use_expert', True)
-        self._configurator.configure_expert_parameter(
-            'expert_preferred_conditions', expert_preferred_conditions)
-        self._configurator.configure_expert_parameter(
-            'expert_forbidden_conditions', expert_forbidden_conditions)
-        self._configurator.configure_expert_parameter(
-            'expert_rules', expert_rules)
-        example_set = create_example_set(
-            values, labels, survival_time_attribute=survival_time_attribute)
-
-        java_model = self._rule_generator.learn(example_set)
-        self.model = RuleSet(java_model)
-        return self.model
-
-    def predict(self, values: Data) -> np.ndarray:
-        if self.model is None:
-            raise ValueError(
-                '"fit" method must be called before calling this method')
-        example_set = create_example_set(values)
-        return self.model._java_object.apply(example_set)
-
-    def set_params(self,
-                   min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                   induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                   pruning_measure: Union[Measures,
-                                          str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                   voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                   max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                   enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                   ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                   max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                   select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
-
-                   extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
-                   extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
-                   induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
-                   induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
-                   consider_other_classes: bool = DEFAULT_PARAMS_VALUE['consider_other_classes'],
-                   preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
-                       'preferred_conditions_per_rule'],
-                   preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE['preferred_attributes_per_rule']) -> object:
-        self._params = dict(
-            min_rule_covered=min_rule_covered,
-            induction_measure=induction_measure,
-            pruning_measure=pruning_measure,
-            voting_measure=voting_measure,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate,
-            extend_using_preferred=extend_using_preferred,
-            extend_using_automatic=extend_using_automatic,
-            induce_using_preferred=induce_using_preferred,
-            induce_using_automatic=induce_using_automatic,
-            consider_other_classes=consider_other_classes,
-            preferred_conditions_per_rule=preferred_conditions_per_rule,
-            preferred_attributes_per_rule=preferred_attributes_per_rule,
-        )
-        self._rule_generator = get_rule_generator(expert=True)
-        self._configurator = RuleGeneratorConfigurator(self._rule_generator)
-        self._rule_generator = self._configurator.configure(**self._params)
-        return self
diff --git a/rulekit/params.py b/rulekit/params.py
index 5411ba9..84af311 100644
--- a/rulekit/params.py
+++ b/rulekit/params.py
@@ -1,11 +1,20 @@
+"""Contains constants and classes for specyfing models parameters
+"""
 from enum import Enum
+from typing import Optional
+from pydantic import BaseModel  # pylint: disable=no-name-in-module
+
+SURVIVAL_TIME_ATTR_ROLE: str = "survival_time"
+CONTRAST_ATTR_ROLE: str = "contrast_attribute"
 
 
 class Measures(Enum):
+    # pylint: disable=invalid-name
     """Enum for different measures used during induction, pruning and voting.
 
     You can ream more about each measure and its implementation
-    `here <https://github.com/adaa-polsl/RuleKit/wiki/4-Quality-and-evaluation#41-rule-quality>`_ . 
+    #41-rule-quality>`_ .
+    `here <https://github.com/adaa-polsl/RuleKit/wiki/4-Quality-and-evaluation
     """
     Accuracy = 'Accuracy'
     BinaryEntropy = 'BinaryEntropy'
@@ -47,3 +56,36 @@ class Measures(Enum):
     WeightedRelativeAccuracy = 'WeightedRelativeAccuracy'
     YAILS = 'YAILS'
     LogRank = 'LogRank'
+
+
+class ModelsParams(BaseModel):
+    """Model for validating models hyperparameters
+    """
+    min_rule_covered: Optional[int]
+    minsupp_new: Optional[int]
+    induction_measure: Measures
+    pruning_measure: Measures
+    voting_measure: Measures
+    max_growing: float
+    enable_pruning: bool
+    ignore_missing: bool
+    max_uncovered_fraction: float
+    select_best_candidate: bool
+
+    extend_using_preferred: Optional[bool]
+    extend_using_automatic: Optional[bool]
+    induce_using_preferred: Optional[bool]
+    induce_using_automatic: Optional[bool]
+    consider_other_classes: Optional[bool]
+    preferred_conditions_per_rule: Optional[int]
+    preferred_attributes_per_rule: Optional[int]
+
+
+class ContrastSetModelParams(ModelsParams):
+    """Model for validating contrast set models hyperparameters
+    """
+    minsupp_all: str
+    max_neg2pos: float
+    max_passes_count: int
+    penalty_strength: float
+    penalty_saturation: float
diff --git a/rulekit/regression.py b/rulekit/regression.py
index 4561a8a..39d8826 100644
--- a/rulekit/regression.py
+++ b/rulekit/regression.py
@@ -1,54 +1,74 @@
-from typing import Union, Any, List, Tuple
+"""Module contaiing classes for regression analysis and prediction.
+"""
+from __future__ import annotations
+from typing import Optional, Union, Iterable
 from numbers import Number
 import numpy as np
 import pandas as pd
 from sklearn import metrics
-from .helpers import PredictionResultMapper
-from .operator import BaseOperator, ExpertKnowledgeOperator, Data, DEFAULT_PARAMS_VALUE
+from ._helpers import PredictionResultMapper
+from ._operator import BaseOperator, ExpertKnowledgeOperator, Data, DEFAULT_PARAMS_VALUE
 from .params import Measures
 
 
 class RuleRegressor(BaseOperator):
     """Regression model."""
 
-    def __init__(self,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                 pruning_measure: Union[Measures,
-                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']):
+    def __init__(
+        self,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
+        pruning_measure: Union[Measures,
+                               str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
+        voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
+        max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+        min_rule_covered: Optional[int] = None,
+    ):
         """
         Parameters
         ----------
-        min_rule_covered : int = 5
-            positive integer representing minimum number of previously uncovered examples to be covered by a new rule
-            (positive examples for classification problems); default: 5
-        induction_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be
+            covered by a new rule (positive examples for classification problems); default: 5
+        induction_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during induction; default measure is correlation
-        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = :class:`rulekit.params.Measures.Correlation`
-            measure used during pruning. Could be user defined (string), for example  :code:`2 * p / n`;
-            default measure is correlation
-        voting_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during pruning. Could be user defined (string), for example  
+            :code:`2 * p / n`; default measure is correlation
+        voting_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during voting; default measure is correlation
         max_growing : int = 0.0
-            non-negative integer representing maximum number of conditions which can be added to the rule in the growing phase
-            (use this parameter for large datasets if execution time is prohibitive); 0 indicates no limit; default: 0,
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
         enable_pruning : bool = True
             enable or disable pruning, default is True.
         ignore_missing : bool = False
-            boolean telling whether missing values should be ignored (by default, a missing value of given attribute is always
-            considered as not fulfilling the condition build upon that attribute); default: False.
+            boolean telling whether missing values should be ignored (by default, a missing value
+            of given attribute is always considered as not fulfilling the condition build upon that
+            attribute); default: False.
         max_uncovered_fraction : float = 0.0
-            Floating-point number from [0,1] interval representing maximum fraction of examples that may remain uncovered by the rule set, default: 0.0.
+            Floating-point number from [0,1] interval representing maximum fraction of examples 
+            that may remain uncovered by the rule set, default: 0.0.
         select_best_candidate : bool = False
-            Flag determining if best candidate should be selected from growing phase; default: False.
+            Flag determining if best candidate should be selected from growing phase; 
+            default: False.
+        min_rule_covered : int = None
+            alias to `minsupp_new`. Parameter is deprecated and will be removed in the next major
+            version, use `minsupp_new`
+
+            .. deprecated:: 1.7.0
+                Use parameter `minsupp_new` instead.
         """
         super().__init__(
+            minsupp_new=minsupp_new,
             min_rule_covered=min_rule_covered,
             induction_measure=induction_measure,
             pruning_measure=pruning_measure,
@@ -57,9 +77,19 @@ def __init__(self,
             enable_pruning=enable_pruning,
             ignore_missing=ignore_missing,
             max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate)
+            select_best_candidate=select_best_candidate
+        )
+
+    def _validate_labels(self, labels: Data):
+        if isinstance(labels, (pd.DataFrame, pd.Series)):
+            first_label = labels.iloc[0]
+        else:
+            first_label = labels[0]
+        if not isinstance(first_label, Number):
+            raise ValueError(
+                'DecisionTreeRegressor requires lables values to be numeric')
 
-    def fit(self, values: Data, labels: Data) -> Any:
+    def fit(self, values: Data, labels: Data) -> RuleRegressor:  # pylint: disable=arguments-differ
         """Train model on given dataset.
 
         Parameters
@@ -72,13 +102,7 @@ def fit(self, values: Data, labels: Data) -> Any:
         -------
         self : RuleRegressor
         """
-        if isinstance(labels, (pd.DataFrame, pd.Series)):
-            first_label = labels.iloc[0]
-        else:
-            first_label = labels[0]
-        if not isinstance(first_label, Number):
-            raise ValueError(
-                'DecisionTreeRegressor requires lables values to be numeric')
+        self._validate_labels(labels)
         super().fit(values, labels)
         return self
 
@@ -122,66 +146,101 @@ def _map_result(self, predicted_example_set) -> np.ndarray:
 class ExpertRuleRegressor(ExpertKnowledgeOperator, RuleRegressor):
     """Expert Regression model."""
 
-    def __init__(self,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
-                 pruning_measure: Union[Measures,
-                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
-                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
-                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+    def __init__(
+        self,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
+        pruning_measure: Union[Measures,
+                               str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
+        voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
+        max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
 
-                 extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
-                 extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
-                 induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
-                 induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
-                 preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
-                     'preferred_conditions_per_rule'],
-                 preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE['preferred_attributes_per_rule']):
+        extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
+        extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
+        induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
+        induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
+        preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
+            'preferred_conditions_per_rule'],
+        preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE[
+            'preferred_attributes_per_rule'],
+        min_rule_covered: Optional[int] = None
+    ):
         """
         Parameters
         ----------
-        min_rule_covered : int = 5
-            positive integer representing minimum number of previously uncovered examples to be covered by a new rule
-            (positive examples for classification problems); default: 5
-        induction_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be
+            covered by a new rule (positive examples for classification problems); default: 5
+        induction_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during induction; default measure is correlation
-        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = :class:`rulekit.params.Measures.Correlation`
-            measure used during pruning. Could be user defined (string), for example  :code:`2 * p / n`;
-            default measure is correlation
-        voting_measure : :class:`rulekit.params.Measures` = :class:`rulekit.params.Measures.Correlation`
+        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during pruning. Could be user defined (string), for example  
+            :code:`2 * p / n`; default measure is correlation
+        voting_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
             measure used during voting; default measure is correlation
         max_growing : int = 0.0
-            non-negative integer representing maximum number of conditions which can be added to the rule in the growing phase
-            (use this parameter for large datasets if execution time is prohibitive); 0 indicates no limit; default: 0,
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase (use this parameter for large datasets if execution time 
+            is prohibitive); 0 indicates no limit; default: 0,
         enable_pruning : bool = True
             enable or disable pruning, default is True.
         ignore_missing : bool = False
-            boolean telling whether missing values should be ignored (by default, a missing value of given attribute is always
-            considered as not fulfilling the condition build upon that attribute); default: False.
+            boolean telling whether missing values should be ignored (by default, a missing value
+            of given attribute is always considered as not fulfilling the condition build upon that
+            attribute); default: False.
         max_uncovered_fraction : float = 0.0
-            Floating-point number from [0,1] interval representing maximum fraction of examples that may remain uncovered by the rule set, default: 0.0.
+            Floating-point number from [0,1] interval representing maximum fraction of examples
+            that may remain uncovered by the rule set, default: 0.0.
         select_best_candidate : bool = False
-            Flag determining if best candidate should be selected from growing phase; default: False.
+            Flag determining if best candidate should be selected from growing phase;
+            default: False.
 
         extend_using_preferred : bool = False
-            boolean indicating whether initial rules should be extended with a use of preferred conditions and attributes; default is False
+            boolean indicating whether initial rules should be extended with a use of preferred
+            conditions and attributes; default is False
         extend_using_automatic : bool = False
-            boolean indicating whether initial rules should be extended with a use of automatic conditions and attributes; default is False
+            boolean indicating whether initial rules should be extended with a use of automatic
+            conditions and attributes; default is False
         induce_using_preferred : bool = False
-            boolean indicating whether new rules should be induced with a use of preferred conditions and attributes; default is False
+            boolean indicating whether new rules should be induced with a use of preferred
+            conditions and attributes; default is False
         induce_using_automatic : bool = False
-            boolean indicating whether new rules should be induced with a use of automatic conditions and attributes; default is False
+            boolean indicating whether new rules should be induced with a use of automatic
+            conditions and attributes; default is False
         preferred_conditions_per_rule : int = None
             maximum number of preferred conditions per rule; default: unlimited,
         preferred_attributes_per_rule : int = None
             maximum number of preferred attributes per rule; default: unlimited.
+        min_rule_covered : int = None
+            alias to `minsupp_new`. Parameter is deprecated and will be removed in the next major
+            version, use `minsupp_new`
+
+            .. deprecated:: 1.7.0
+                Use parameter `minsupp_new` instead.
         """
+        RuleRegressor.__init__(
+            self,
+            minsupp_new=minsupp_new,
+            min_rule_covered=min_rule_covered,
+            induction_measure=induction_measure,
+            pruning_measure=pruning_measure,
+            voting_measure=voting_measure,
+            max_growing=max_growing,
+            enable_pruning=enable_pruning,
+            ignore_missing=ignore_missing,
+            max_uncovered_fraction=max_uncovered_fraction,
+            select_best_candidate=select_best_candidate
+        )
         ExpertKnowledgeOperator.__init__(
             self,
+            minsupp_new=minsupp_new,
             min_rule_covered=min_rule_covered,
             induction_measure=induction_measure,
             pruning_measure=pruning_measure,
@@ -199,14 +258,15 @@ def __init__(self,
             preferred_attributes_per_rule=preferred_attributes_per_rule
         )
 
-    def fit(self,
-            values: Data,
-            labels: Data,
+    def fit(  # pylint: disable=arguments-differ
+        self,
+        values: Data,
+        labels: Data,
 
-            expert_rules: List[Union[str, Tuple[str, str]]] = None,
-            expert_preferred_conditions: List[Union[str,
-                                                    Tuple[str, str]]] = None,
-            expert_forbidden_conditions: List[Union[str, Tuple[str, str]]] = None) -> Any:
+        expert_rules: list[Union[str, tuple[str, str]]] = None,
+        expert_preferred_conditions: list[Union[str, tuple[str, str]]] = None,
+        expert_forbidden_conditions: list[Union[str, tuple[str, str]]] = None
+    ) -> ExpertRuleRegressor:
         """Train model on given dataset.
 
         Parameters
@@ -217,25 +277,21 @@ def fit(self,
             target values
 
         expert_rules : List[Union[str, Tuple[str, str]]]
-             set of initial rules, either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            set of initial rules, either passed as a list of strings representing rules or as list
+            of tuples where first element is name of the rule and second one is rule string.
         expert_preferred_conditions : List[Union[str, Tuple[str, str]]]
-             multiset of preferred conditions (used also for specifying preferred attributes by using special value Any). Either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            multiset of preferred conditions (used also for specifying preferred attributes by 
+            using special value Any). Either passed as a list of strings representing rules or as 
+            list of tuples where first element is name of the rule and second one is rule string.
         expert_forbidden_conditions : List[Union[str, Tuple[str, str]]]
-             set of forbidden conditions (used also for specifying forbidden attributes by using special valye Any). Either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            set of forbidden conditions (used also for specifying forbidden attributes by using 
+            special valye Any). Either passed as a list of strings representing rules or as list 
+            of tuples where first element is name of the rule and second one is rule string.
         Returns
         -------
         self : ExpertRuleRegressor
         """
-        if isinstance(labels, (pd.DataFrame, pd.Series)):
-            first_label = labels.iloc[0]
-        else:
-            first_label = labels[0]
-        if not isinstance(first_label, Number):
-            raise ValueError(
-                'ExpertRuleRegressor requires lables values to be numeric')
+        self._validate_labels(labels)
         return ExpertKnowledgeOperator.fit(
             self,
             values,
@@ -247,3 +303,149 @@ def fit(self,
 
     def predict(self, values: Data) -> np.ndarray:
         return self._map_result(ExpertKnowledgeOperator.predict(self, values))
+
+
+class ContrastSetRuleRegressor(BaseOperator):
+    """Contrast set regression model."""
+
+    def __init__(self,
+                 minsupp_all: Iterable[float] = DEFAULT_PARAMS_VALUE['minsupp_all'],
+                 max_neg2pos: float = DEFAULT_PARAMS_VALUE['max_neg2pos'],
+                 max_passes_count: int = DEFAULT_PARAMS_VALUE['max_passes_count'],
+                 penalty_strength: float = DEFAULT_PARAMS_VALUE['penalty_strength'],
+                 penalty_saturation: float = DEFAULT_PARAMS_VALUE['penalty_saturation'],
+
+                 minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+                 induction_measure: Measures = DEFAULT_PARAMS_VALUE['induction_measure'],
+                 pruning_measure: Union[Measures,
+                                        str] = DEFAULT_PARAMS_VALUE['pruning_measure'],
+                 voting_measure: Measures = DEFAULT_PARAMS_VALUE['voting_measure'],
+                 max_growing: float = DEFAULT_PARAMS_VALUE['max_growing'],
+                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+                 ):
+        """
+        Parameters
+        ----------
+        minsupp_all: Iterable[float]
+            a minimum positive support of a contrast set (p/P). When multiple values are specified,
+            a metainduction is performed; Default and recommended sequence is: 0.8, 0.5, 0.2, 0.1
+        max_neg2pos: float
+            a maximum ratio of negative to positive supports (nP/pN); Default is 0.5
+        max_passes_count: int
+            a maximum number of sequential covering passes for a single minsupp-all; Default is 5
+        penalty_strength: float
+            (s) - penalty strength; Default is 0.5
+        penalty_saturation: float
+            the value of p_new / P at which penalty reward saturates; Default is 0.2.
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be 
+            covered by a new rule (positive examples for classification problems); default: 5
+        induction_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during induction; default measure is correlation
+        pruning_measure : Union[:class:`rulekit.params.Measures`, str] = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during pruning. Could be user defined (string), for example
+            :code:`2 * p / n`; default measure is correlation
+        voting_measure : :class:`rulekit.params.Measures` = \
+            :class:`rulekit.params.Measures.Correlation`
+            measure used during voting; default measure is correlation
+        max_growing : int = 0.0
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
+        enable_pruning : bool = True
+            enable or disable pruning, default is True.
+        ignore_missing : bool = False
+            boolean telling whether missing values should be ignored (by default, a missing value 
+            of given attribute is always considered as not fulfilling the condition build upon that
+            attribute); default: False.
+        max_uncovered_fraction : float = 0.0
+            Floating-point number from [0,1] interval representing maximum fraction of examples
+            that may remain uncovered by the rule set, default: 0.0.
+        select_best_candidate : bool = False
+            Flag determining if best candidate should be selected from growing phase;
+            default: False.
+        """
+        super().__init__(
+            minsupp_all=minsupp_all,
+            max_neg2pos=max_neg2pos,
+            max_passes_count=max_passes_count,
+            penalty_strength=penalty_strength,
+            penalty_saturation=penalty_saturation,
+            minsupp_new=minsupp_new,
+            induction_measure=induction_measure,
+            pruning_measure=pruning_measure,
+            voting_measure=voting_measure,
+            max_growing=max_growing,
+            enable_pruning=enable_pruning,
+            ignore_missing=ignore_missing,
+            max_uncovered_fraction=max_uncovered_fraction,
+            select_best_candidate=select_best_candidate)
+        self.contrast_attribute: str = None
+
+    def fit(self, values: Data, labels: Data, contrast_attribute: str) -> ContrastSetRuleRegressor:  # pylint: disable=arguments-differ
+        """Train model on given dataset.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+        labels : :class:`rulekit.operator.Data`
+            target values
+        contrast_attribute: str 
+            group attribute
+        Returns
+        -------
+        self : ContrastSetRuleRegressor
+        """
+        RuleRegressor._validate_labels(  # pylint: disable=protected-access
+            self, labels
+        )
+        super().fit(values, labels, contrast_attribute=contrast_attribute)
+        self.contrast_attribute = contrast_attribute
+        return self
+
+    def predict(self, values: Data) -> np.ndarray:
+        """Perform prediction and returns predicted values.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+
+        Returns
+        -------
+        result : np.ndarray
+            predicted values
+        """
+        return RuleRegressor.predict(self, values)
+
+    def score(self, values: Data, labels: Data) -> float:
+        """Return the coefficient of determination R2 of the prediction
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+        labels : :class:`rulekit.operator.Data`
+            true target values
+
+        Returns
+        -------
+        score : float
+            R2 of self.predict(values) wrt. labels.
+        """
+        return RuleRegressor.score(self, values, labels)
+
+    def __getstate__(self) -> dict:
+        return {**BaseOperator.__getstate__(self), **{
+            'contrast_attribute': self.contrast_attribute,
+        }}
+
+    def __setstate__(self, state: dict):
+        BaseOperator.__setstate__(self, state)
+        self.contrast_attribute = state['contrast_attribute']
diff --git a/rulekit/rules.py b/rulekit/rules.py
index a8a7ef9..fe26db6 100644
--- a/rulekit/rules.py
+++ b/rulekit/rules.py
@@ -1,4 +1,6 @@
-from typing import Union, List
+"""Contains classes representing rules and rulesets.
+"""
+from typing import Union
 import numpy as np
 from .params import Measures
 from .stats import RuleStatistics, RuleSetStatistics
@@ -28,12 +30,12 @@ def weighted_n(self) -> float:
         return self._java_object.getWeighted_n()
 
     @property
-    def weighted_P(self) -> float:
+    def weighted_P(self) -> float:  # pylint: disable=invalid-name
         """Number of positives in the training set (accounting weights)."""
         return self._java_object.getWeighted_P()
 
     @property
-    def weighted_N(self) -> float:
+    def weighted_N(self) -> float:  # pylint: disable=invalid-name
         """Number of negatives in the training set (accounting weights)."""
         return self._java_object.getWeighted_N()
 
@@ -51,7 +53,7 @@ def stats(self) -> RuleStatistics:
 
     def get_covering_information(self) -> dict:
         """Returns information about rule covering
-        
+
         Returns
         -------
         covering_data : dict
@@ -74,6 +76,8 @@ def __str__(self):
 
 
 class InductionParameters:
+    """Induction parameters.
+    """
 
     def __init__(self, java_object):
         self._java_object = java_object
@@ -86,14 +90,26 @@ def __init__(self, java_object):
 
     @property
     def induction_measure(self) -> Union[Measures, str]:
+        """
+        Returns:
+            Union[Measures, str]: Measure used for induction
+        """
         return InductionParameters._get_measure_str(self._java_object.getInductionMeasure())
 
     @property
     def pruning_measure(self) -> Union[Measures, str]:
+        """
+        Returns:
+            Union[Measures, str]: Measure used for pruning
+        """
         return InductionParameters._get_measure_str(self._java_object.getPruningMeasure())
 
     @property
     def voting_measure(self) -> Union[Measures, str]:
+        """
+        Returns:
+            Union[Measures, str]: Measure used for voting
+        """
         return InductionParameters._get_measure_str(self._java_object.getVotingMeasure())
 
     @staticmethod
@@ -151,17 +167,18 @@ def covering(self, example_set) -> np.ndarray:
         """:meta private:"""
         res = []
         for rule in self.rules:
-            covering_info = rule._java_object.coversUnlabelled(example_set)
+            covering_info = rule._java_object.coversUnlabelled(  # pylint: disable=protected-access
+                example_set
+            )
             covered_examples_indexes = []
             covered_examples_indexes += covering_info
             res.append(covered_examples_indexes)
         return np.array(res, dtype=object)
 
     @property
-    def rules(self) -> List[Rule]:
+    def rules(self) -> list[Rule]:
         """List of rules objects."""
-        rules = self._java_object.getRules()
-        return list(map(lambda rule: Rule(rule), rules))
+        return [Rule(java_rule) for java_rule in self._java_object.getRules()]
 
     def calculate_conditions_count(self) -> float:
         """
@@ -213,7 +230,7 @@ def calculate_significance(self, alpha: float) -> dict:
         Parameters
         ----------
         alpha : float
-        
+
         Returns
         -------
         count: float
@@ -231,7 +248,8 @@ def calculate_significance_fdr(self, alpha: float) -> dict:
         -------
         count: dict
             Significance of the rule set with false discovery rate correction. Dictionary contains
-            two fields: *fraction* (fraction of rules significant at assumed level) and *p* (average p-value of all rules).
+            two fields: *fraction* (fraction of rules significant at assumed level) and *p* 
+            (average p-value of all rules).
         """
         significance = self._java_object.calculateSignificanceFDR(alpha)
         return {
@@ -244,8 +262,9 @@ def calculate_significance_fwer(self, alpha: float) -> dict:
         Returns
         -------
         count: dict
-            Significance of the rule set with familiy-wise error rate correction. Dictionary contains
-            two fields: *fraction* (fraction of rules significant at assumed level) and *p* (average p-value of all rules).
+            Significance of the rule set with familiy-wise error rate correction. Dictionary 
+            contains two fields: *fraction* (fraction of rules significant at assumed level) 
+            and *p* (average p-value of all rules).
         """
         significance = self._java_object.calculateSignificanceFWER(alpha)
         return {
diff --git a/rulekit/stats.py b/rulekit/stats.py
index 58bd97e..b5148f1 100644
--- a/rulekit/stats.py
+++ b/rulekit/stats.py
@@ -1,3 +1,5 @@
+"""Contains classes describing rules and rulesets statistics and metrics
+"""
 
 
 class RuleStatistics:
@@ -20,6 +22,7 @@ class RuleStatistics:
     """
 
     def __init__(self, rule):
+        # pylint: disable=invalid-name
         self.p = rule.weighted_p
         self.n = rule.weighted_n
         self.P = rule.weighted_P
@@ -69,13 +72,16 @@ class RuleSetStatistics:
     fraction_significant : float
         Fraction of rules significant at assumed level
     fraction_FDR_significant : float
-        Fraction of rules significant, set with false discovery rate correction, at assumed level.
+        Fraction of rules significant, set with false discovery rate correction,
+        at assumed level.
     fraction_FWER_significant : float
-        Fraction of rules significant, set with familiy-wise error rate correction, at assumed level.
+        Fraction of rules significant, set with familiy-wise error rate correction,
+        at assumed level.
     """
     SIGNIFICANCE_LEVEL = 0.05
 
     def __init__(self, ruleset):
+        # pylint: disable=invalid-name
         self.time_total_s = ruleset.total_time
         self.time_growing_s = ruleset.growing_time
         self.time_pruning_s = ruleset.pruning_time
@@ -97,28 +103,41 @@ def __init__(self, ruleset):
 
         self.fraction_significant = ruleset.calculate_significance(
             RuleSetStatistics.SIGNIFICANCE_LEVEL)['fraction']
-        self.fraction_FDR_significant = ruleset.calculate_significance_fdr(RuleSetStatistics.SIGNIFICANCE_LEVEL)[
-            'fraction']
-        self.fraction_FWER_significant = ruleset.calculate_significance_fwer(RuleSetStatistics.SIGNIFICANCE_LEVEL)[
-            'fraction']
+        self.fraction_FDR_significant = ruleset.calculate_significance_fdr(
+            RuleSetStatistics.SIGNIFICANCE_LEVEL
+        )['fraction']
+        self.fraction_FWER_significant = ruleset.calculate_significance_fwer(
+            RuleSetStatistics.SIGNIFICANCE_LEVEL
+        )['fraction']
 
     def __str__(self):
-        return f'Time total [s]: {self.time_total_s}\n' + \
-               f'Time growing [s]: {self.time_growing_s}\n' + \
-               f'Time pruning [s]: {self.time_pruning_s}\n' + \
-               '\n' + \
-               f'Rules count: {self.rules_count}\n' + \
-               f'Conditions per rule: {self.conditions_per_rule}\n' + \
-               f'Induced conditions per rule: {self.induced_conditions_per_rule}\n' + \
-               '\n' + \
-               f'Average rule coverage: {self.avg_rule_coverage}\n' + \
-               f'Average rule precision: {self.avg_rule_precision}\n' + \
-               f'Average rule quality: {self.avg_rule_quality}\n' + \
-               '\n' + \
-               f'pvalue: {self.pvalue}\n' + \
-               f'FDR pvalue: {self.FDR_pvalue}\n' + \
-               f'FWER pvalue: {self.FWER_pvalue}\n' + \
-               '\n' + \
-               f'Fraction {RuleSetStatistics.SIGNIFICANCE_LEVEL} significant: {self.fraction_significant}\n' + \
-               f'Fraction {RuleSetStatistics.SIGNIFICANCE_LEVEL} FDR significant: {self.fraction_FDR_significant}\n' + \
-               f'Fraction {RuleSetStatistics.SIGNIFICANCE_LEVEL} FWER significant: {self.fraction_FWER_significant}\n'
+        return (
+            f'Time total [s]: {self.time_total_s}\n' +
+            f'Time growing [s]: {self.time_growing_s}\n' +
+            f'Time pruning [s]: {self.time_pruning_s}\n' +
+            '\n' +
+            f'Rules count: {self.rules_count}\n' +
+            f'Conditions per rule: {self.conditions_per_rule}\n' +
+            f'Induced conditions per rule: {self.induced_conditions_per_rule}\n' +
+            '\n' +
+            f'Average rule coverage: {self.avg_rule_coverage}\n' +
+            f'Average rule precision: {self.avg_rule_precision}\n' +
+            f'Average rule quality: {self.avg_rule_quality}\n' +
+            '\n' +
+            f'pvalue: {self.pvalue}\n' +
+            f'FDR pvalue: {self.FDR_pvalue}\n' +
+            f'FWER pvalue: {self.FWER_pvalue}\n' +
+            '\n' +
+            (
+                f'Fraction {RuleSetStatistics.SIGNIFICANCE_LEVEL} ' +
+                f'significant: {self.fraction_significant}\n'
+            ) +
+            (
+                f'Fraction {RuleSetStatistics.SIGNIFICANCE_LEVEL} ' +
+                f'FDR significant: {self.fraction_FDR_significant}\n'
+            ) +
+            (
+                f'Fraction {RuleSetStatistics.SIGNIFICANCE_LEVEL} ' +
+                f'FWER significant: {self.fraction_FWER_significant}\n'
+            )
+        )
diff --git a/rulekit/survival.py b/rulekit/survival.py
index 7ad7f84..e9e9acf 100644
--- a/rulekit/survival.py
+++ b/rulekit/survival.py
@@ -1,51 +1,96 @@
-from typing import Any, Union, Tuple, List
+"""Module contaiing classes for survival analysis and prediction.
+"""
+from __future__ import annotations
+from typing import Optional, Union, Iterable
 import numpy as np
 import pandas as pd
 from jpype import JClass
-
-from .helpers import PredictionResultMapper, RuleGeneratorConfigurator, get_rule_generator, create_example_set
-from .operator import BaseOperator, ExpertKnowledgeOperator, Data, DEFAULT_PARAMS_VALUE
+from pydantic import BaseModel  # pylint: disable=no-name-in-module
+
+from ._helpers import (
+    PredictionResultMapper,
+    RuleGeneratorConfigurator,
+    get_rule_generator,
+    create_example_set
+)
+from ._operator import BaseOperator, ExpertKnowledgeOperator, Data, DEFAULT_PARAMS_VALUE
+from .params import ContrastSetModelParams
 from .rules import RuleSet
 
 
+class SurvivalModelsParams(BaseModel):
+    """Model for validating survival models hyperparameters
+    """
+    survival_time_attr: Optional[str]
+    minsupp_new: int
+    max_growing: int
+    enable_pruning: bool
+    ignore_missing: bool
+    max_uncovered_fraction: float
+    select_best_candidate: bool
+    min_rule_covered: Optional[int]
+
+    extend_using_preferred: Optional[bool]
+    extend_using_automatic: Optional[bool]
+    induce_using_preferred: Optional[bool]
+    induce_using_automatic: Optional[bool]
+    consider_other_classes: Optional[bool]
+    preferred_conditions_per_rule: Optional[int]
+    preferred_attributes_per_rule: Optional[int]
+
 
 class SurvivalRules(BaseOperator):
     """Survival model."""
 
-    def __init__(self,
-                 survival_time_attr: str = None,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']):
+    def __init__(  # pylint: disable=super-init-not-called
+        self,
+        survival_time_attr: str = None,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+        min_rule_covered: Optional[int] = None
+    ):
         """
         Parameters
         ----------
         survival_time_attr : str
-            name of column containing survival time data (use when data passed to model is padnas dataframe).
-        min_rule_covered : int = 5
-            positive integer representing minimum number of previously uncovered examples to be covered by a new rule 
-            (positive examples for classification problems); default: 5
+            name of column containing survival time data (use when data passed to model is padnas
+            dataframe).
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be
+            covered by a new rule (positive examples for classification problems); default: 5
         max_growing : int = 0.0
-            non-negative integer representing maximum number of conditions which can be added to the rule in the growing phase 
-            (use this parameter for large datasets if execution time is prohibitive); 0 indicates no limit; default: 0,
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase  (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
         enable_pruning : bool = True
             enable or disable pruning, default is True.
         ignore_missing : bool = False
-            boolean telling whether missing values should be ignored (by default, a missing value of given attribute is always 
-            considered as not fulfilling the condition build upon that attribute); default: False.
+            boolean telling whether missing values should be ignored (by default, a missing value 
+            of given attribute is always considered as not fulfilling the condition build upon that
+            attribute); default: False.
         max_uncovered_fraction : float = 0.0
-            Floating-point number from [0,1] interval representing maximum fraction of examples that may remain uncovered by the rule set, default: 0.0.
+            Floating-point number from [0,1] interval representing maximum fraction of examples 
+            that may remain uncovered by the rule set, default: 0.0.
         select_best_candidate : bool = False
-            Flag determining if best candidate should be selected from growing phase; default: False.
+            Flag determining if best candidate should be selected from growing phase; 
+            default: False.
+        min_rule_covered : int = None
+            alias to `minsupp_new`. Parameter is deprecated and will be removed in the next major
+            version, use `minsupp_new`
+
+            .. deprecated:: 1.7.0
+                Use parameter `minsupp_new` instead.
         """
         self._params = None
         self._rule_generator = None
         self._configurator = None
         self.set_params(
             survival_time_attr=survival_time_attr,
+            minsupp_new=minsupp_new,
             min_rule_covered=min_rule_covered,
             max_growing=max_growing,
             enable_pruning=enable_pruning,
@@ -54,26 +99,25 @@ def __init__(self,
             select_best_candidate=select_best_candidate)
         self.model: RuleSet = None
 
-    def set_params(self,
-                   survival_time_attr: str = None,
-                   min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                   max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
-                   enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                   ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                   max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                   select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']) -> object:
+    def set_params(
+        self,
+        **kwargs
+    ) -> object:
         """Set models hyperparameters. Parameters are the same as in constructor."""
-        self.survival_time_attr = survival_time_attr
+        # params validatio
+        params = SurvivalModelsParams(**kwargs)
+        self.survival_time_attr = params.survival_time_attr
         self._rule_generator = get_rule_generator()
         self._configurator = RuleGeneratorConfigurator(self._rule_generator)
         self._params = dict(
-            survival_time_attr=survival_time_attr,
-            min_rule_covered=min_rule_covered,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate
+            survival_time_attr=params.survival_time_attr,
+            minsupp_new=params.minsupp_new,
+            min_rule_covered=params.min_rule_covered,
+            max_growing=params.max_growing,
+            enable_pruning=params.enable_pruning,
+            ignore_missing=params.ignore_missing,
+            max_uncovered_fraction=params.max_uncovered_fraction,
+            select_best_candidate=params.select_best_candidate
         )
         self._rule_generator = self._configurator.configure(**self._params)
         return self
@@ -92,10 +136,23 @@ def _append_survival_time_columns(values, survival_time) -> str:
                 row.append(survival_time[index])
         else:
             raise ValueError(
-                'Data values must be instance of either pandas DataFrame, numpy array or list')
+                'Data values must be instance of either pandas DataFrame, numpy array or list'
+            )
         return ''
 
-    def fit(self, values: Data, labels: Data, survival_time: Data = None) -> Any:
+    def _prepare_survival_attribute(self, survival_time: Data, values: Data) -> str:
+        if self.survival_time_attr is None and survival_time is None:
+            raise ValueError(
+                'No "survival_time" attribute name was specified. ' +
+                'Specify it using method set_params'
+            )
+        if survival_time is not None:
+            return SurvivalRules._append_survival_time_columns(
+                values, survival_time)
+        else:
+            return self.survival_time_attr
+
+    def fit(self, values: Data, labels: Data, survival_time: Data = None) -> SurvivalRules:  # pylint: disable=arguments-differ
         """Train model on given dataset.
 
         Parameters
@@ -105,20 +162,15 @@ def fit(self, values: Data, labels: Data, survival_time: Data = None) -> Any:
         labels : :class:`rulekit.operator.Data`
             survival status
         survival_time: :class:`rulekit.operator.Data`
-            data about survival time. Could be omitted when *survival_time_attr* parameter was specified.
+            data about survival time. Could be omitted when *survival_time_attr* parameter 
+            was specified.
 
         Returns
         -------
         self : SurvivalRules
         """
-        if self.survival_time_attr is None and survival_time is None:
-            raise ValueError(
-                'No "survival_time" attribute name was specified. Specify it using method set_params')
-        if survival_time is not None:
-            survival_time_attribute = SurvivalRules._append_survival_time_columns(
-                values, survival_time)
-        else:
-            survival_time_attribute = self.survival_time_attr
+        survival_time_attribute = self._prepare_survival_attribute(
+            survival_time, values)
         super().fit(values, labels, survival_time_attribute)
         return self
 
@@ -133,15 +185,20 @@ def predict(self, values: Data) -> np.ndarray:
         Returns
         -------
         result : np.ndarray
-            Each row represent single example from dataset and contains estimated survival function for that example. Estimated survival function is returned as a dictionary containing times and corresponding probabilities.
+            Each row represent single example from dataset and contains estimated survival function
+            for that example. Estimated survival function is returned as a dictionary containing 
+            times and corresponding probabilities.
         """
         return PredictionResultMapper.map_survival(super().predict(values))
 
     def score(self, values: Data, labels: Data, survival_time: Data = None) -> float:
-        """Return the Integrated Brier Score on the given dataset and labels(event status indicator).
+        """Return the Integrated Brier Score on the given dataset and labels 
+        (event status indicator).
 
-        Integrated Brier Score (IBS) - the Brier score (BS) represents the squared difference between true event status at time T and predicted event status at that time; 
-        the Integrated Brier score summarizes the prediction error over all observations and over all times in a test set.
+        Integrated Brier Score (IBS) - the Brier score (BS) represents the squared difference 
+        between true event status at time T and predicted event status at that time; 
+        the Integrated Brier score summarizes the prediction error over all observations and over
+        all times in a test set.
 
         Parameters
         ----------
@@ -150,7 +207,8 @@ def score(self, values: Data, labels: Data, survival_time: Data = None) -> float
         labels : :class:`rulekit.operator.Data`
             survival status
         survival_time: :class:`rulekit.operator.Data`
-            data about survival time. Could be omitted when *survival_time_attr* parameter was specified
+            data about survival time. Could be omitted when *survival_time_attr* parameter was
+            specified
 
         Returns
         -------
@@ -158,85 +216,104 @@ def score(self, values: Data, labels: Data, survival_time: Data = None) -> float
             Integrated Brier Score of self.predict(values) wrt. labels.
         """
 
-        if self.survival_time_attr is None and survival_time is None:
-            raise ValueError(
-                'No "survival_time" attribute name was specified. Specify it using method set_params')
-        if survival_time is not None:
-            survival_time_attribute = SurvivalRules._append_survival_time_columns(
-                values, survival_time)
-        else:
-            survival_time_attribute = self.survival_time_attr
-
+        survival_time_attribute = self._prepare_survival_attribute(
+            survival_time, values)
         example_set = create_example_set(
             values, labels,  survival_time_attribute=survival_time_attribute)
 
-        predicted_example_set = self.model._java_object.apply(example_set)
+        predicted_example_set = self.model._java_object.apply(  # pylint: disable=protected-access
+            example_set
+        )
 
-        IntegratedBrierScore = JClass(
-            'adaa.analytics.rules.logic.quality.IntegratedBrierScore')
-        integratedBrierScore = IntegratedBrierScore()
-        integratedBrierScore.startCounting(predicted_example_set, True)
-        return integratedBrierScore.getMikroAverage()
+        IntegratedBrierScore = JClass(  # pylint: disable=invalid-name
+            'adaa.analytics.rules.logic.quality.IntegratedBrierScore'
+        )
+        integrated_brier_score = IntegratedBrierScore()
+        integrated_brier_score.startCounting(predicted_example_set, True)
+        return integrated_brier_score.getMikroAverage()
 
 
 class ExpertSurvivalRules(ExpertKnowledgeOperator, SurvivalRules):
     """Expert Survival model."""
 
-    def __init__(self,
-                 survival_time_attr: str = None,
-                 min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                 max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
-                 enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                 ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                 max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                 select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
-
-                 extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
-                 extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
-                 induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
-                 induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
-                 preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
-                     'preferred_conditions_per_rule'],
-                 preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE['preferred_attributes_per_rule']):
+    def __init__(  # pylint: disable=super-init-not-called
+        self,
+        survival_time_attr: str = None,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
+
+        extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
+        extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
+        induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
+        induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
+        preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
+            'preferred_conditions_per_rule'],
+        preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE[
+            'preferred_attributes_per_rule'],
+        min_rule_covered: Optional[int] = None
+    ):
         """
         Parameters
         ----------
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be
+            covered by a new rule (positive examples for classification problems); default: 5
         survival_time_attr : str
-            name of column containing survival time data (use when data passed to model is padnas dataframe).
+            name of column containing survival time data (use when data passed to model is pandas
+            dataframe).
         min_rule_covered : int = 5
-            positive integer representing minimum number of previously uncovered examples to be covered by a new rule 
-            (positive examples for classification problems); default: 5
+            positive integer representing minimum number of previously uncovered examples to be 
+            covered by a new rule (positive examples for classification problems); default: 5
         max_growing : int = 0.0
-            non-negative integer representing maximum number of conditions which can be added to the rule in the growing phase 
-            (use this parameter for large datasets if execution time is prohibitive); 0 indicates no limit; default: 0,
+            non-negative integer representing maximum number of conditions which can be added to
+            the rule in the growing phase (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
         enable_pruning : bool = True
             enable or disable pruning, default is True.
         ignore_missing : bool = False
-            boolean telling whether missing values should be ignored (by default, a missing value of given attribute is always 
-            considered as not fulfilling the condition build upon that attribute); default: False.
+            boolean telling whether missing values should be ignored (by default, a missing value
+            of given attribute is always considered as not fulfilling the condition build upon that
+            attribute); default: False.
         max_uncovered_fraction : float = 0.0
-            Floating-point number from [0,1] interval representing maximum fraction of examples that may remain uncovered by the rule set, default: 0.0.
+            Floating-point number from [0,1] interval representing maximum fraction of examples 
+            that may remain uncovered by the rule set, default: 0.0.
         select_best_candidate : bool = False
-            Flag determining if best candidate should be selected from growing phase; default: False.
+            Flag determining if best candidate should be selected from growing phase; 
+            default: False.
 
         extend_using_preferred : bool = False
-            boolean indicating whether initial rules should be extended with a use of preferred conditions and attributes; default is False
+            boolean indicating whether initial rules should be extended with a use of preferred
+            conditions and attributes; default is False
         extend_using_automatic : bool = False
-            boolean indicating whether initial rules should be extended with a use of automatic conditions and attributes; default is False
+            boolean indicating whether initial rules should be extended with a use of automatic
+            conditions and attributes; default is False
         induce_using_preferred : bool = False
-            boolean indicating whether new rules should be induced with a use of preferred conditions and attributes; default is False
+            boolean indicating whether new rules should be induced with a use of preferred 
+            conditions and attributes; default is False
         induce_using_automatic : bool = False
-            boolean indicating whether new rules should be induced with a use of automatic conditions and attributes; default is False
+            boolean indicating whether new rules should be induced with a use of automatic 
+            conditions and attributes; default is False
         preferred_conditions_per_rule : int = None
             maximum number of preferred conditions per rule; default: unlimited,
         preferred_attributes_per_rule : int = None
             maximum number of preferred attributes per rule; default: unlimited.
+        min_rule_covered : int = None
+            alias to `minsupp_new`. Parameter is deprecated and will be removed in the next major
+            version, use `minsupp_new`
+
+            .. deprecated:: 1.7.0
+                Use parameter `minsupp_new` instead.
         """
         self._params = None
         self._rule_generator = None
         self._configurator = None
         self.set_params(
             survival_time_attr=survival_time_attr,
+            minsupp_new=minsupp_new,
             min_rule_covered=min_rule_covered,
             max_growing=max_growing,
             enable_pruning=enable_pruning,
@@ -252,54 +329,44 @@ def __init__(self,
         )
         self.model: RuleSet = None
 
-    def set_params(self,
-                   survival_time_attr: str = None,
-                   min_rule_covered: int = DEFAULT_PARAMS_VALUE['min_rule_covered'],
-                   max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
-                   enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
-                   ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
-                   max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
-                   select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate'],
-
-                   extend_using_preferred: bool = DEFAULT_PARAMS_VALUE['extend_using_preferred'],
-                   extend_using_automatic: bool = DEFAULT_PARAMS_VALUE['extend_using_automatic'],
-                   induce_using_preferred: bool = DEFAULT_PARAMS_VALUE['induce_using_preferred'],
-                   induce_using_automatic: bool = DEFAULT_PARAMS_VALUE['induce_using_automatic'],
-                   preferred_conditions_per_rule: int = DEFAULT_PARAMS_VALUE[
-                       'preferred_conditions_per_rule'],
-                   preferred_attributes_per_rule: int = DEFAULT_PARAMS_VALUE['preferred_attributes_per_rule']) -> object:
-
-        self.survival_time_attr = survival_time_attr
+    def set_params(  # pylint: disable=arguments-differ
+        self,
+        **kwargs
+    ) -> object:
+        params = SurvivalModelsParams(**kwargs)
+        self.survival_time_attr = params.survival_time_attr
 
         self._params = dict(
-            survival_time_attr=survival_time_attr,
-            min_rule_covered=min_rule_covered,
-            max_growing=max_growing,
-            enable_pruning=enable_pruning,
-            ignore_missing=ignore_missing,
-            max_uncovered_fraction=max_uncovered_fraction,
-            select_best_candidate=select_best_candidate,
-            extend_using_preferred=extend_using_preferred,
-            extend_using_automatic=extend_using_automatic,
-            induce_using_preferred=induce_using_preferred,
-            induce_using_automatic=induce_using_automatic,
-            preferred_conditions_per_rule=preferred_conditions_per_rule,
-            preferred_attributes_per_rule=preferred_attributes_per_rule,
+            survival_time_attr=params.survival_time_attr,
+            minsupp_new=params.minsupp_new,
+            min_rule_covered=params.min_rule_covered,
+            max_growing=params.max_growing,
+            enable_pruning=params.enable_pruning,
+            ignore_missing=params.ignore_missing,
+            max_uncovered_fraction=params.max_uncovered_fraction,
+            select_best_candidate=params.select_best_candidate,
+            extend_using_preferred=params.extend_using_preferred,
+            extend_using_automatic=params.extend_using_automatic,
+            induce_using_preferred=params.induce_using_preferred,
+            induce_using_automatic=params.induce_using_automatic,
+            preferred_conditions_per_rule=params.preferred_conditions_per_rule,
+            preferred_attributes_per_rule=params.preferred_attributes_per_rule,
         )
         self._rule_generator = get_rule_generator(expert=True)
         self._configurator = RuleGeneratorConfigurator(self._rule_generator)
         self._rule_generator = self._configurator.configure(**self._params)
         return self
 
-    def fit(self,
-            values: Data,
-            labels: Data,
-            survival_time: Data = None,
+    def fit(  # pylint: disable=arguments-differ
+        self,
+        values: Data,
+        labels: Data,
+        survival_time: Data = None,
 
-            expert_rules: List[Union[str, Tuple[str, str]]] = None,
-            expert_preferred_conditions: List[Union[str,
-                                                    Tuple[str, str]]] = None,
-            expert_forbidden_conditions: List[Union[str, Tuple[str, str]]] = None) -> Any:
+        expert_rules: list[Union[str, tuple[str, str]]] = None,
+        expert_preferred_conditions: list[Union[str, tuple[str, str]]] = None,
+        expert_forbidden_conditions: list[Union[str, tuple[str, str]]] = None
+    ) -> ExpertSurvivalRules:
         """Train model on given dataset.
 
         Parameters
@@ -309,30 +376,28 @@ def fit(self,
         labels : Data
             survival status
         survival_time: :class:`rulekit.operator.Data`
-            data about survival time. Could be omitted when *survival_time_attr* parameter was specified.
+            data about survival time. Could be omitted when *survival_time_attr* parameter was
+            specified.
 
         expert_rules : List[Union[str, Tuple[str, str]]]
-             set of initial rules, either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            set of initial rules, either passed as a list of strings representing rules or as list
+            of tuples where first
+            element is name of the rule and second one is rule string.
         expert_preferred_conditions : List[Union[str, Tuple[str, str]]]
-             multiset of preferred conditions (used also for specifying preferred attributes by using special value Any). Either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            multiset of preferred conditions (used also for specifying preferred attributes by 
+            using special value Any). Either passed as a list of strings representing rules or as
+            list of tuples where first element is name of the rule and second one is rule string.
         expert_forbidden_conditions : List[Union[str, Tuple[str, str]]]
-             set of forbidden conditions (used also for specifying forbidden attributes by using special valye Any). Either passed as a list of strings representing rules or as list of tuples where first
-             element is name of the rule and second one is rule string.
+            set of forbidden conditions (used also for specifying forbidden attributes by using 
+            special valye Any). Either passed as a list of strings representing rules or as list
+            of tuples where first element is name of the rule and second one is rule string.
 
         Returns
         -------
         self : ExpertSurvivalRules
         """
-        if self.survival_time_attr is None and survival_time is None:
-            raise ValueError('No "survival_time" attribute name was specified. '
-                             'Specify it or pass its values by "survival_time" parameter.')
-        if survival_time is not None:
-            survival_time_attribute = SurvivalRules._append_survival_time_columns(
-                values, survival_time)
-        else:
-            survival_time_attribute = self.survival_time_attr
+        survival_time_attribute = SurvivalRules._prepare_survival_attribute(
+            self, survival_time, values)
         return ExpertKnowledgeOperator.fit(
             self,
             values=values,
@@ -345,3 +410,189 @@ def fit(self,
 
     def predict(self, values: Data) -> np.ndarray:
         return PredictionResultMapper.map_survival(ExpertKnowledgeOperator.predict(self, values))
+
+
+class SurvivalContrastSetModelParams(ContrastSetModelParams, SurvivalModelsParams):
+    """Model for validating survival contrast sets models hyperparameters
+     """
+
+
+class ContrastSetSurvivalRules(BaseOperator):
+    """Contrast set survival model."""
+
+    def __init__(  # pylint: disable=super-init-not-called
+        self,
+        minsupp_all: Iterable[float] = DEFAULT_PARAMS_VALUE['minsupp_all'],
+        max_neg2pos: float = DEFAULT_PARAMS_VALUE['max_neg2pos'],
+        max_passes_count: int = DEFAULT_PARAMS_VALUE['max_passes_count'],
+        penalty_strength: float = DEFAULT_PARAMS_VALUE['penalty_strength'],
+        penalty_saturation: float = DEFAULT_PARAMS_VALUE['penalty_saturation'],
+
+        survival_time_attr: str = None,
+        minsupp_new: int = DEFAULT_PARAMS_VALUE['minsupp_new'],
+        max_growing: int = DEFAULT_PARAMS_VALUE['max_growing'],
+        enable_pruning: bool = DEFAULT_PARAMS_VALUE['enable_pruning'],
+        ignore_missing: bool = DEFAULT_PARAMS_VALUE['ignore_missing'],
+        max_uncovered_fraction: float = DEFAULT_PARAMS_VALUE['max_uncovered_fraction'],
+        select_best_candidate: bool = DEFAULT_PARAMS_VALUE['select_best_candidate']
+    ):
+        """
+        Parameters
+        ----------
+        minsupp_all: Iterable[float]
+            a minimum positive support of a contrast set (p/P). When multiple values are specified,
+            a metainduction is performed; Default and recommended sequence is: 0.8, 0.5, 0.2, 0.1
+        max_neg2pos: float
+            a maximum ratio of negative to positive supports (nP/pN); Default is 0.5
+        max_passes_count: int
+            a maximum number of sequential covering passes for a single minsupp-all; Default is 5
+        penalty_strength: float
+            (s) - penalty strength; Default is 0.5
+        penalty_saturation: float
+            the value of p_new / P at which penalty reward saturates; Default is 0.2.
+        survival_time_attr : str
+            name of column containing survival time data (use when data passed to model is pandas
+            dataframe).
+        minsupp_new : int = 5
+            positive integer representing minimum number of previously uncovered examples to be 
+            covered by a new rule  (positive examples for classification problems); default: 5
+        max_growing : int = 0.0
+            non-negative integer representing maximum number of conditions which can be added to 
+            the rule in the growing phase (use this parameter for large datasets if execution time
+            is prohibitive); 0 indicates no limit; default: 0,
+        enable_pruning : bool = True
+            enable or disable pruning, default is True.
+        ignore_missing : bool = False
+            boolean telling whether missing values should be ignored (by default, a missing value 
+            of given attribute is always considered as not fulfilling the condition build upon that
+            attribute); default: False.
+        max_uncovered_fraction : float = 0.0
+            Floating-point number from [0,1] interval representing maximum fraction of examples 
+            that may remain uncovered by the rule set, default: 0.0.
+        select_best_candidate : bool = False
+            Flag determining if best candidate should be selected from growing phase;
+            default: False.
+        """
+        if minsupp_all is not None and len(minsupp_all) > 0:
+            minsupp_all = ' '.join([
+                str(e) for e in minsupp_all
+            ])
+        self._params = None
+        self._rule_generator = None
+        self._configurator = None
+        self.contrast_attribute: str = None
+        self.set_params(
+            minsupp_all=minsupp_all,
+            max_neg2pos=max_neg2pos,
+            max_passes_count=max_passes_count,
+            penalty_strength=penalty_strength,
+            penalty_saturation=penalty_saturation,
+            survival_time_attr=survival_time_attr,
+            minsupp_new=minsupp_new,
+            max_growing=max_growing,
+            enable_pruning=enable_pruning,
+            ignore_missing=ignore_missing,
+            max_uncovered_fraction=max_uncovered_fraction,
+            select_best_candidate=select_best_candidate)
+        self.model: RuleSet = None
+
+    def set_params(self, **kwargs) -> object:
+        """Set models hyperparameters. Parameters are the same as in constructor."""
+        # params validation
+        params = SurvivalContrastSetModelParams(**kwargs)
+        self.survival_time_attr = params.survival_time_attr
+        self._rule_generator = get_rule_generator()
+        self._configurator = RuleGeneratorConfigurator(self._rule_generator)
+        self._params = dict(
+            minsupp_all=params.minsupp_all,
+            max_neg2pos=params.max_neg2pos,
+            max_passes_count=params.max_passes_count,
+            penalty_strength=params.penalty_strength,
+            penalty_saturation=params.penalty_saturation,
+            survival_time_attr=params.survival_time_attr,
+            minsupp_new=params.minsupp_new,
+            max_growing=params.max_growing,
+            enable_pruning=params.enable_pruning,
+            ignore_missing=params.ignore_missing,
+            max_uncovered_fraction=params.max_uncovered_fraction,
+            select_best_candidate=params.select_best_candidate
+        )
+        self._rule_generator = self._configurator.configure(**self._params)
+        return self
+
+    def fit(  # pylint: disable=arguments-renamed
+        self,
+        values: Data,
+        labels: Data,
+        contrast_attribute: str,
+        survival_time: Data = None
+    ) -> ContrastSetSurvivalRules:
+        """Train model on given dataset.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+        labels : :class:`rulekit.operator.Data`
+            survival status
+        contrast_attribute: str 
+            group attribute
+        survival_time: :class:`rulekit.operator.Data`
+            data about survival time. Could be omitted when *survival_time_attr* parameter 
+            was specified.
+
+        Returns
+        -------
+        self : ContrastSetSurvivalRules
+        """
+        survival_time_attribute = SurvivalRules._prepare_survival_attribute(  # pylint: disable=protected-access
+            self, survival_time, values)
+        super().fit(
+            values, labels,
+            survival_time_attribute=survival_time_attribute,
+            contrast_attribute=contrast_attribute)
+        self.contrast_attribute = contrast_attribute
+        return self
+
+    def predict(self, values: Data) -> np.ndarray:
+        """Perform prediction and return estimated survival function for each example.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+
+        Returns
+        -------
+        result : np.ndarray
+            Each row represent single example from dataset and contains estimated survival function
+            for that example. Estimated survival function is returned as a dictionary containing 
+            times and corresponding probabilities.
+        """
+        return PredictionResultMapper.map_survival(super().predict(values))
+
+    def score(self, values: Data, labels: Data, survival_time: Data = None) -> float:
+        """Return the Integrated Brier Score on the given dataset and 
+        labels(event status indicator).
+
+        Integrated Brier Score (IBS) - the Brier score (BS) represents the squared difference
+        between true event status at time T and predicted event status at that time; 
+        the Integrated Brier score summarizes the prediction error over all observations and 
+        over all times in a test set.
+
+        Parameters
+        ----------
+        values : :class:`rulekit.operator.Data`
+            attributes
+        labels : :class:`rulekit.operator.Data`
+            survival status
+        survival_time: :class:`rulekit.operator.Data`
+            data about survival time. Could be omitted when *survival_time_attr* parameter was
+            specified
+
+        Returns
+        -------
+        score : float
+            Integrated Brier Score of self.predict(values) wrt. labels.
+        """
+        return SurvivalRules.score(self, values, labels, survival_time=survival_time)


Test patch:
diff --git a/tests/resources.py b/tests/resources.py
index bc82797..9c139c9 100644
--- a/tests/resources.py
+++ b/tests/resources.py
@@ -1,7 +1,6 @@
 import requests
 import os
 import sys
-from typing import List
 
 dir_path = os.path.dirname(os.path.realpath(__file__))
 
@@ -30,7 +29,7 @@ def create_folders():
         os.mkdir(REPORTS_DIRECTORY_PATH)
         
 
-def download_files(content: List, directory_path: str):
+def download_files(content: list, directory_path: str):
     for item in content:
         file_name = item['name']
         download_url = item['download_url']
diff --git a/tests/test_classifier.py b/tests/test_classifier.py
index 0693565..39b6645 100644
--- a/tests/test_classifier.py
+++ b/tests/test_classifier.py
@@ -12,10 +12,6 @@
 
 class TestClassifier(unittest.TestCase):
 
-    @classmethod
-    def setUpClass(cls):
-        RuleKit.init()
-
     def test_classification_accuracy_on_iris(self):
         scikit_clf = scikit.DecisionTreeClassifier()
         rulekit_clf = classification.RuleClassifier()
@@ -118,10 +114,6 @@ def test_compare_with_java_results(self):
 
 class TestExperClassifier(unittest.TestCase):
 
-    @classmethod
-    def setUpClass(cls):
-        RuleKit.init()
-
     def test_compare_with_java_results(self):
         test_cases = get_test_cases('ClassificationExpertSnCTest')
 
diff --git a/tests/utils.py b/tests/utils.py
index 9afd45e..33cd1ba 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,16 +1,16 @@
+import os
+import re
+from typing import Union
 from xml.etree import ElementTree
 import pandas as pd
 from scipy.io.arff import loadarff
 from sklearn import metrics
 from jpype import JClass
-from typing import List, Dict, Tuple, Union
-import os
-import re
 
-from rulekit.helpers import create_example_set, _fix_missing_values
+
+from rulekit._helpers import create_example_set
 from rulekit.rules import Rule
 
-import os
 
 dir_path = os.path.dirname(os.path.realpath(__file__))
 
@@ -27,6 +27,12 @@
 EXPERIMENTS_PATH = '/resources/config'
 
 
+def _fix_missing_values(column) -> None:
+    for i, value in enumerate(column.values):
+        if value == b'?':
+            column.values[i] = None
+
+
 class ExampleSetWrapper:
 
     def __init__(self, values, labels):
@@ -34,7 +40,7 @@ def __init__(self, values, labels):
         self.labels = labels
         self.example_set = create_example_set(values, labels)
 
-    def get_data(self) -> Tuple:
+    def get_data(self) -> tuple:
         return self.values, self.labels
 
 
@@ -77,10 +83,10 @@ def __init__(self, file_name: str):
 class TestCase:
 
     def __init__(self):
-        self.param_config: Dict[str, object] = None
+        self.param_config: dict[str, object] = None
         self._reference_report: TestReport = None
         self._example_set: ExampleSetWrapper = None
-        self.induction_params: Dict = None
+        self.induction_params: dict = None
         self.knowledge: Knowledge = None
 
         self.name: str = None
@@ -93,14 +99,17 @@ def __init__(self):
     @property
     def example_set(self) -> ExampleSetWrapper:
         if self._example_set is None:
-            self._example_set = load_arff_to_example_set(self.data_set_file_path, self.label_attribute)
+            self._example_set = load_arff_to_example_set(
+                self.data_set_file_path, self.label_attribute)
         return self._example_set
 
     @property
     def reference_report(self) -> TestReport:
         if self._reference_report is None:
-            ExampleSetMetaData = JClass('com.rapidminer.operator.ports.metadata.ExampleSetMetaData')
-            reader = TestReportReader(f'{self.report_file_path}.txt', ExampleSetMetaData(self._example_set.example_set))
+            ExampleSetMetaData = JClass(
+                'com.rapidminer.operator.ports.metadata.ExampleSetMetaData')
+            reader = TestReportReader(f'{self.report_file_path}.txt', ExampleSetMetaData(
+                self._example_set.example_set))
             self._reference_report = reader.read()
             reader.close()
         return self._reference_report
@@ -119,8 +128,8 @@ class TestConfig:
 
     def __init__(self):
         self.name: str = None
-        self.parameter_configs: Dict[str, Dict[str, object]] = None
-        self.datasets: List[DataSetConfig] = None
+        self.parameter_configs: dict[str, dict[str, object]] = None
+        self.datasets: list[DataSetConfig] = None
 
 
 class TestConfigParser:
@@ -145,17 +154,18 @@ class TestConfigParser:
     ]
 
     def __init__(self):
-        self.tests_configs: Dict[str, TestConfig] = {}
+        self.tests_configs: dict[str, TestConfig] = {}
         self.root: ElementTree = None
 
     def _parse_survival_time(self, element) -> Union[str, None]:
-        survival_time_element = element.find(TestConfigParser.SURVIVAL_TIME_ROLE)
+        survival_time_element = element.find(
+            TestConfigParser.SURVIVAL_TIME_ROLE)
         if element.find(TestConfigParser.SURVIVAL_TIME_ROLE) is not None:
             return survival_time_element.text
         else:
             return None
 
-    def _parse_experts_rules_parameters(self, elements) -> List[Tuple]:
+    def _parse_experts_rules_parameters(self, elements) -> list[tuple]:
         expert_rules = []
         for element in elements:
             rule_name = element.attrib['name']
@@ -163,19 +173,21 @@ def _parse_experts_rules_parameters(self, elements) -> List[Tuple]:
             expert_rules.append((rule_name, rule_content))
         return expert_rules if len(expert_rules) > 0 else None
 
-    def _check_ambigous_data_sets_names(self, data_sets_configs: List[DataSetConfig]):
-        dict = {}
+    def _check_ambigous_data_sets_names(self, data_sets_configs: list[DataSetConfig]):
+        dictionary = {}
         for element in data_sets_configs:
-            dict[element.name] = None
-        if len(dict.keys()) < len(data_sets_configs):
+            dictionary[element.name] = None
+        if len(dictionary.keys()) < len(data_sets_configs):
             raise ValueError('Datasets are ambigous')
 
     def _parse_data_set(self, element) -> DataSetConfig:
         data_set_config = DataSetConfig()
-        data_set_config.label_attribute = element.find(TestConfigParser.LABEL_KEY).text
+        data_set_config.label_attribute = element.find(
+            TestConfigParser.LABEL_KEY).text
         train_element = element.find(TestConfigParser.TRAINING_KEY)
         train_element = train_element.find(TestConfigParser.TRAIN_KEY)
-        data_set_config.train_file_name = train_element.find(TestConfigParser.IN_FILE_KEY).text
+        data_set_config.train_file_name = train_element.find(
+            TestConfigParser.IN_FILE_KEY).text
         data_set_config.name = element.attrib.get('name', None)
         if data_set_config.name is None:
             file_name = os.path.basename(data_set_config.train_file_name)
@@ -183,27 +195,29 @@ def _parse_data_set(self, element) -> DataSetConfig:
         data_set_config.survival_time = self._parse_survival_time(element)
         return data_set_config
 
-    def _parse_data_sets(self, element) -> List[DataSetConfig]:
+    def _parse_data_sets(self, element) -> list[DataSetConfig]:
         data_set_configs = []
         node = element.find(TestConfigParser.DATASETS_KEY)
         for element in node.findall(TestConfigParser.DATASET_KEY):
             data_set_configs.append(self._parse_data_set(element))
         return data_set_configs
 
-    def parse_test_parameters(self, element) -> Dict[str, object]:
+    def parse_test_parameters(self, element) -> dict[str, object]:
         params = {}
         for param_node in element.findall(TestConfigParser.PARAM_KEY):
             name: str = param_node.attrib['name']
             if name in TestConfigParser.EXPERTS_RULES_PARAMETERS_NAMES:
-                value = self._parse_experts_rules_parameters(param_node.findall(TestConfigParser.ENTRY_KEY))
+                value = self._parse_experts_rules_parameters(
+                    param_node.findall(TestConfigParser.ENTRY_KEY))
             else:
                 value = param_node.text
             params[name] = value
         return params
 
-    def _parse_test_parameters_sets(self, element) -> Dict[str, Dict[str, object]]:
+    def _parse_test_parameters_sets(self, element) -> dict[str, dict[str, object]]:
         parameters_sets = {}
-        params_sets_node = element.findall(TestConfigParser.PARAMETERS_SET_KEY)[0]
+        params_sets_node = element.findall(
+            TestConfigParser.PARAMETERS_SET_KEY)[0]
         for param_set in params_sets_node.findall(TestConfigParser.PARAMETERS_KEY):
             name: str = param_set.attrib['name']
             parameters_sets[name] = self.parse_test_parameters(param_set)
@@ -211,12 +225,13 @@ def _parse_test_parameters_sets(self, element) -> Dict[str, Dict[str, object]]:
 
     def _parse_test(self, element) -> TestConfig:
         test_config = TestConfig()
-        test_config.parameter_configs = self._parse_test_parameters_sets(element)
+        test_config.parameter_configs = self._parse_test_parameters_sets(
+            element)
         test_config.datasets = self._parse_data_sets(element)
         test_config.name = element.attrib['name']
         return test_config
 
-    def parse(self, file_path: str) -> Dict[str, TestConfig]:
+    def parse(self, file_path: str) -> dict[str, TestConfig]:
         self.tests_configs = {}
         self.root = ElementTree.parse(file_path).getroot()
         if self.root.tag == 'test':
@@ -234,7 +249,7 @@ class TestCaseFactory:
     def _make_test_case(
             self,
             test_case_name: str,
-            params: Dict[str, object],
+            params: dict[str, object],
             data_set_config: DataSetConfig) -> TestCase:
         test_case = TestCase()
         self.fix_params_typing(params)
@@ -256,7 +271,7 @@ def fix_params_typing(self, params: dict):
             if not 'measure' in key:
                 params[key] = int(float(value))
 
-    def make(self, tests_configs: Dict[str, TestConfig], report_dir_path: str) -> List[TestCase]:
+    def make(self, tests_configs: dict[str, TestConfig], report_dir_path: str) -> list[TestCase]:
         test_cases = []
         for key in tests_configs.keys():
             test_config = tests_configs[key]
@@ -264,8 +279,10 @@ def make(self, tests_configs: Dict[str, TestConfig], report_dir_path: str) -> Li
                 for data_set_config in test_config.datasets:
                     params = test_config.parameter_configs[config_name]
                     test_case_name = f'{key}.{config_name}.{data_set_config.name}'
-                    test_config.parameter_configs[config_name].pop('use_expert', None)
-                    expert_rules = test_config.parameter_configs[config_name].pop('expert_rules', None)
+                    test_config.parameter_configs[config_name].pop(
+                        'use_expert', None)
+                    expert_rules = test_config.parameter_configs[config_name].pop(
+                        'expert_rules', None)
                     preferred_conditions = test_config.parameter_configs[config_name].pop('expert_preferred_conditions',
                                                                                           None)
                     forbidden_conditions = test_config.parameter_configs[config_name].pop('expert_forbidden_conditions',
@@ -298,7 +315,7 @@ class TestReportReader:
     def __init__(self, file_name: str, example_set_meta_data):
         self.example_set_meta_data = example_set_meta_data
         self.file_name = file_name
-        self._file = f = open(file_name, "r")
+        self._file = open(file_name, encoding='utf-8', mode='r')
 
     def _read_rules(self, test_report: TestReport):
         rules = []
@@ -320,7 +337,8 @@ def read(self) -> TestReport:
             elif line == '':
                 continue
             else:
-                raise ValueError(f'Invalid report file format for file: {self.file_name}')
+                raise ValueError(
+                    f'Invalid report file format for file: {self.file_name}')
         return test_report
 
     def close(self):
@@ -330,7 +348,7 @@ def close(self):
 class TestReportWriter:
 
     def __init__(self, file_name: str):
-        self._file = f = open(file_name, "w")
+        self._file = open(file_name, encoding='utf-8', mode="w")
         if not os.path.exists(REPORTS_OUT_DIRECTORY_PATH):
             os.makedirs(REPORTS_OUT_DIRECTORY_PATH)
 
@@ -344,9 +362,9 @@ def close(self):
         self._file.close()
 
 
-def get_test_cases(class_name: str) -> List[TestCase]:
+def get_test_cases(class_name: str) -> list[TestCase]:
     if not os.path.exists(DATA_IN_DIRECTORY_PATH):
-        raise Exception('''\n
+        raise RuntimeError('''\n
 Test resources directory dosen't exist. Check if 'tests/resources/' directory exist.
 
 If you're running tests for the first time you need to download resources folder from RuleKit repository by running:
@@ -360,7 +378,7 @@ def _get_rule_string(rule: Rule) -> str:
     return re.sub(r'(\\[[^\\]]*\\]$)|(\\([^\\)]*\\)$)', '', str(rule))
 
 
-def assert_rules_are_equals(expected: List[str], actual: List[str]):
+def assert_rules_are_equals(expected: list[str], actual: list[str]):
     def sanitize_rule_string(rule_string: str) -> str:
         return re.sub(r'(\t)|(\n)|(\[[^\]]*\]$)', '', rule_string)
 
@@ -375,11 +393,12 @@ def sanitize_rule_string(rule_string: str) -> str:
         if key in dictionary:
             dictionary[key] = dictionary[key] + 1
         else:
-            pass
-            raise AssertionError('Actual ruleset contains rules not present in expected ruleset')
+            raise AssertionError(
+                'Actual ruleset contains rules not present in expected ruleset')
     for key in dictionary.keys():
         if dictionary[key] == 0:
-            raise AssertionError('Ruleset are not equal, some rules are missing')
+            raise AssertionError(
+                'Ruleset are not equal, some rules are missing')
         elif dictionary[key] > 1:
             raise AssertionError('Somes rules were duplicated')
 
@@ -396,7 +415,8 @@ def assert_score_is_greater(prediction, expected, threshold: float):
         labels = expected.to_numpy().astype(int)
     if isinstance(prediction[0], float):
         labels = expected.to_numpy().astype(float)
-    explained_variance_score = metrics.explained_variance_score(labels, prediction)
+    explained_variance_score = metrics.explained_variance_score(
+        labels, prediction)
 
     if explained_variance_score <= threshold:
         raise AssertionError(f'Score should be greater than {threshold}')