Repository: FormalLanguageConstrainedPathQuerying/CFPQ_Data (language: python)

Issue:
Graphs information



Discussion / hints:

Fix patch:
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 00000000..797be343
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,514 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-whitelist=
+
+# Specify a score threshold to be exceeded before program exits with error.
+fail-under=10.0
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+init-hook='import sys; sys.path.append("/path/to/root")'
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use.
+jobs=8
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=invalid-name,
+        missing-module-docstring,
+        too-many-locals,
+        import-error,
+        too-many-instance-attributes
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'error', 'warning', 'refactor', and 'convention'
+# which contain the number of messages in each category, as well as 'statement'
+# which is the total number of statements analyzed. This score is used by the
+# global evaluation report (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it work,
+# install the python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+          bar,
+          baz,
+          toto,
+          tutu,
+          tata
+
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style.
+#class-attribute-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+           j,
+           k,
+           ex,
+           Run,
+           _
+
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style.
+#variable-rgx=
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+          _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+      XXX,
+      TODO
+
+# Regular expression of note tags to take in consideration.
+#notes-rgx=
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp,
+                      __post_init__
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method.
+max-args=5
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in an if statement (see R0916).
+max-bool-expr=5
+
+# Maximum number of branch for function / method body.
+max-branches=12
+
+# Maximum number of locals for function / method body.
+max-locals=15
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=6
+
+# Maximum number of statements in function / method body.
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=optparse,tkinter.tix
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled).
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled).
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "BaseException, Exception".
+overgeneral-exceptions=BaseException,
+                       Exception
diff --git a/Dockerfile b/Dockerfile
index 78d3e9ad..2f80e789 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,13 @@
 FROM ubuntu:latest
 
-RUN apt-get -y update && apt-get install -y \
+RUN add-apt-repository ppa:deadsnakes/ppa
+RUN apt -y update && \
+    apt install -y \
+    python3.9 \
     python3-pip \
-    git
+    git \
+    default-jre \
+    default-jdk
 
 COPY . /CFPQ_Data
 
diff --git a/cfpq_data/__init__.py b/cfpq_data/__init__.py
index 9bead91a..0ef48363 100644
--- a/cfpq_data/__init__.py
+++ b/cfpq_data/__init__.py
@@ -1,11 +1,7 @@
-from cfpq_data.src.graphs.FullGraph import FullGraph
-from cfpq_data.src.graphs.MemoryAliases import MemoryAliases
-from cfpq_data.src.graphs.RDF import RDF
-from cfpq_data.src.graphs.ScaleFree import ScaleFree
-from cfpq_data.src.graphs.SparseGraph import SparseGraph
-from cfpq_data.src.graphs.WorstCase import WorstCase
-from cfpq_data.src.tools.LUBM.converter import LUBMGraph
-from cfpq_data.src.tools.gen_RPQ.gen import GenRPQGraph
-from cfpq_data.src.tools.grammar2cnf.grammar2cnf import Grammar2Cnf
-from cfpq_data.src.tools.graph2txt.graph2txt import Graph2TxtGraph
-from cfpq_data.src.tools.redis_rdf.main import RedisRDFGraph
+from cfpq_data.src.graphs.full_graph import FullGraph
+from cfpq_data.src.graphs.lubm_graph import LUBM
+from cfpq_data.src.graphs.memory_aliases_graph import MemoryAliases
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.graphs.scale_free_graph import ScaleFree
+from cfpq_data.src.graphs.sparse_graph import SparseGraph
+from cfpq_data.src.graphs.worst_case_graph import WorstCase
diff --git a/cfpq_data/cfpq_data.py b/cfpq_data/cfpq_data.py
deleted file mode 100755
index 2849cf49..00000000
--- a/cfpq_data/cfpq_data.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import argparse
-
-from cfpq_data.src.graphs.FullGraph import FullGraph
-from cfpq_data.src.graphs.MemoryAliases import MemoryAliases
-from cfpq_data.src.graphs.RDF import RDF
-from cfpq_data.src.graphs.ScaleFree import ScaleFree
-from cfpq_data.src.graphs.SparseGraph import SparseGraph
-from cfpq_data.src.graphs.WorstCase import WorstCase
-from cfpq_data.src.tools.LUBM.converter import LUBMGraph
-from cfpq_data.src.tools.gen_RPQ.gen import GenRPQGraph
-from cfpq_data.src.tools.grammar2cnf.grammar2cnf import Grammar2Cnf
-from cfpq_data.src.tools.graph2txt.graph2txt import Graph2TxtGraph
-from cfpq_data.src.tools.redis_rdf.main import RedisRDFGraph
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser()
-    subparsers = parser.add_subparsers(dest='tool', required=True)
-
-    tools = {
-        'RDF': RDF
-        , 'MemoryAliases': MemoryAliases
-        , 'ScaleFree': ScaleFree
-        , 'FullGraph': FullGraph
-        , 'WorstCase': WorstCase
-        , 'SparseGraph': SparseGraph
-        , 'LUBM': LUBMGraph
-        , 'graph2txt': Graph2TxtGraph
-        , 'grammar2cnf': Grammar2Cnf
-        , 'gen_RPQ': GenRPQGraph
-        , 'redis_rdf': RedisRDFGraph
-    }
-
-    for name, tool in tools.items():
-        subparser = subparsers.add_parser(name)
-        tool.init_cmd_parser(subparser)
-
-    args = parser.parse_args()
-    tools[args.tool].eval_cmd_parser(args)
diff --git a/cfpq_data/cmd_parser.py b/cfpq_data/cmd_parser.py
new file mode 100755
index 00000000..b7ee3901
--- /dev/null
+++ b/cfpq_data/cmd_parser.py
@@ -0,0 +1,42 @@
+import argparse
+
+from cfpq_data.src.graphs.full_graph import FullGraph
+from cfpq_data.src.graphs.lubm_graph import LUBM
+from cfpq_data.src.graphs.memory_aliases_graph import MemoryAliases
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.graphs.scale_free_graph import ScaleFree
+from cfpq_data.src.graphs.sparse_graph import SparseGraph
+from cfpq_data.src.graphs.worst_case_graph import WorstCase
+
+
+def cmd_parser() -> None:
+    """
+    Command line utility for loading graphs
+
+    :return: None
+    :rtype: None
+    """
+
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers(dest='tool', required=True)
+
+    tools = {
+        'RDF': RDF
+        , 'MemoryAliases': MemoryAliases
+        , 'ScaleFree': ScaleFree
+        , 'FullGraph': FullGraph
+        , 'WorstCase': WorstCase
+        , 'SparseGraph': SparseGraph
+        , 'LUBM': LUBM
+    }
+
+    for name, tool in tools.items():
+        subparser = subparsers.add_parser(name)
+        tool.init_cmd_parser(subparser)
+
+    args = parser.parse_args()
+    tools[args.tool].eval_cmd_parser(args)
+
+
+if __name__ == '__main__':
+    cmd_parser()
diff --git a/cfpq_data/config.py b/cfpq_data/config.py
new file mode 100644
index 00000000..a4d1b92d
--- /dev/null
+++ b/cfpq_data/config.py
@@ -0,0 +1,15 @@
+import json
+from pathlib import Path
+
+PACKAGE_NAME = 'cfpq_data'
+
+MAIN_FOLDER = Path(__file__).parent
+
+with open(MAIN_FOLDER / 'release_notes.json', 'r') as input_file:
+    RELEASE_INFO = json.load(input_file)
+
+PACKAGE_VERSION = RELEASE_INFO['version']
+
+RDF_CONFIG = RELEASE_INFO['RDF_Config']
+MEMORYALIASES_CONFIG = RELEASE_INFO['MemoryAliases_Config']
+GENERATORS_CONFIG = RELEASE_INFO['Generators_Config']
diff --git a/cfpq_data/src/tools/LUBM/__init__.py b/cfpq_data/data/.gitkeep
similarity index 100%
rename from cfpq_data/src/tools/LUBM/__init__.py
rename to cfpq_data/data/.gitkeep
diff --git a/cfpq_data/src/tools/grammar2cnf/__init__.py b/cfpq_data/data/FullGraph/.gitkeep
similarity index 100%
rename from cfpq_data/src/tools/grammar2cnf/__init__.py
rename to cfpq_data/data/FullGraph/.gitkeep
diff --git a/cfpq_data/src/tools/graph2txt/__init__.py b/cfpq_data/data/FullGraph/Grammars/.gitkeep
similarity index 100%
rename from cfpq_data/src/tools/graph2txt/__init__.py
rename to cfpq_data/data/FullGraph/Grammars/.gitkeep
diff --git a/cfpq_data/data/FullGraph/Graphs/.gitkeep b/cfpq_data/data/FullGraph/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/LUBM/.gitkeep b/cfpq_data/data/LUBM/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/LUBM/Grammars/.gitkeep b/cfpq_data/data/LUBM/Grammars/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/LUBM/Graphs/.gitkeep b/cfpq_data/data/LUBM/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/MemoryAliases/.gitkeep b/cfpq_data/data/MemoryAliases/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/MemoryAliases/Grammars/.gitkeep b/cfpq_data/data/MemoryAliases/Grammars/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/MemoryAliases/Graphs/.gitkeep b/cfpq_data/data/MemoryAliases/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/RDF/.gitkeep b/cfpq_data/data/RDF/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/RDF/Grammars/.gitkeep b/cfpq_data/data/RDF/Grammars/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/RDF/Graphs/.gitkeep b/cfpq_data/data/RDF/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/ScaleFree/.gitkeep b/cfpq_data/data/ScaleFree/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/ScaleFree/Grammars/.gitkeep b/cfpq_data/data/ScaleFree/Grammars/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/ScaleFree/Graphs/.gitkeep b/cfpq_data/data/ScaleFree/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/SparseGraph/.gitkeep b/cfpq_data/data/SparseGraph/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/SparseGraph/Grammars/.gitkeep b/cfpq_data/data/SparseGraph/Grammars/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/SparseGraph/Graphs/.gitkeep b/cfpq_data/data/SparseGraph/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/WorstCase/.gitkeep b/cfpq_data/data/WorstCase/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/WorstCase/Grammars/.gitkeep b/cfpq_data/data/WorstCase/Grammars/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/data/WorstCase/Graphs/.gitkeep b/cfpq_data/data/WorstCase/Graphs/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/release_notes.json b/cfpq_data/release_notes.json
index 8c0767c4..0dedb319 100644
--- a/cfpq_data/release_notes.json
+++ b/cfpq_data/release_notes.json
@@ -43,5 +43,54 @@
     "security_afterInline": "1TY206_ikpQL35AuWP_6KqAdxPGQQfKLN",
     "sound_afterInline": "1KdolYE4XB929vXpv8ISu8xNtGM4UmacC",
     "wc": "1dQD5zbsj7FvpCJ0dh1LoH9cfdMTb3mcm"
+  },
+  "RDF_Config": {
+    "http://www.w3.org/2000/01/rdf-schema#subClassOf": "SCO",
+    "http://www.w3.org/1999/02/22-rdf-syntax-ns#type": "T",
+    "http://www.w3.org/2004/02/skos/core#broaderTransitive": "BT",
+    "default": "UNKNOWN"
+  },
+  "MemoryAliases_Config": {
+    "http://yacc/A": "A",
+    "http://yacc/D": "D",
+    "default": "UNKNOWN"
+  },
+  "LUBM_Config": {
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#subOrganizationOf": "SO",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#worksFor": "WF",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#headOf": "HF",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#teacherOf": "TF",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#memberOf": "MF",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#advisor": "AD",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#takesCourse": "TC",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#publicationAuthor": "PA",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#undergraduateDegreeFrom": "UG",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#mastersDegreeFrom": "MD",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#doctoralDegreeFrom": "DD",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#emailAddress": "EA",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#name": "NM",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#researchInterest": "RI",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#telephone": "TP",
+    "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#teachingAssistantOf": "TA",
+    "default": "UNKNOWN"
+  },
+  "Generators_Config": {
+    "http://yacc/rdf-schema#A": "A",
+    "http://yacc/rdf-schema#AR": "AR",
+    "http://yacc/rdf-schema#B": "B",
+    "http://yacc/rdf-schema#BR": "BR",
+    "http://yacc/rdf-schema#C": "C",
+    "http://yacc/rdf-schema#CR": "CR",
+    "http://yacc/rdf-schema#D": "D",
+    "http://yacc/rdf-schema#DR": "DR",
+    "A": "http://yacc/rdf-schema#A",
+    "AR": "http://yacc/rdf-schema#AR",
+    "B": "http://yacc/rdf-schema#B",
+    "BR": "http://yacc/rdf-schema#BR",
+    "C": "http://yacc/rdf-schema#C",
+    "CR": "http://yacc/rdf-schema#CR",
+    "D": "http://yacc/rdf-schema#D",
+    "DR": "http://yacc/rdf-schema#DR",
+    "default": "UNKNOWN"
   }
 }
diff --git a/cfpq_data/src/graphs/FullGraph.py b/cfpq_data/src/graphs/FullGraph.py
deleted file mode 100644
index a0cce6b2..00000000
--- a/cfpq_data/src/graphs/FullGraph.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.graphs.Graph import Graph
-from cfpq_data.src.tools.CmdParser import CmdParser
-from cfpq_data.src.tools.rdf_helper import write_to_rdf, add_rdf_edge
-from cfpq_data.src.utils import *
-
-FULL_GRAPH_TO_GEN = [
-    10
-    , 50
-    , 100
-    , 200
-    , 500
-    , 1000
-    , 2000
-    , 5000
-    , 10000
-    , 25000
-    , 50000
-    , 80000
-]
-
-
-class FullGraph(Graph, CmdParser):
-    graphs = {}
-
-    def __init__(self, vertices_number):
-        self.dirname = add_graph_dir('FullGraph')
-        self.basename = f'fullgraph_{vertices_number}.xml'
-
-        path_to_graph = gen_cycle_graph(self.dirname, vertices_number)
-
-        self.graph = rdflib.Graph()
-        self.graph.load(path_to_graph)
-
-        self.vertices_number = len(self.graph.all_nodes())
-        self.number_of_edges = len(self.graph)
-
-        self.file_size = os.path.getsize(path_to_graph)
-        self.file_name, self.file_extension = os.path.splitext(self.basename)
-
-        FullGraph.graphs[self.basename] = path_to_graph
-
-    def get_metadata(self):
-        return {
-            'name': self.basename
-            , 'path': self.dirname
-            , 'version': get_info()['version']
-            , 'vertices': self.vertices_number
-            , 'edges': self.number_of_edges
-            , 'size of file': self.file_size
-        }
-
-    def save_metadata(self):
-        with open(f'{self.dirname}/{self.file_name}_meta.json', 'w') as metadata_file:
-            json.dump(self.get_metadata(), metadata_file, indent=4)
-
-    @staticmethod
-    def init_cmd_parser(parser):
-        parser.add_argument(
-            '-p'
-            , '--preset'
-            , action='store_true'
-            , help='Load preset FullGraph graphs from dataset'
-        )
-        parser.add_argument(
-            '-n'
-            , '--vertices_number'
-            , required=False
-            , type=int
-            , help='Number of vertices of FullGraph graph'
-        )
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.preset is False and args.vertices_number is None:
-            print("One of -p/--preset, -n/--vertices_number required")
-            exit()
-
-        if args.preset is True:
-            for n in tqdm(FULL_GRAPH_TO_GEN, desc='Full graphs generation'):
-                FullGraph(n).save_metadata()
-
-        if args.vertices_number is not None:
-            graph = FullGraph(args.vertices_number)
-            graph.save_metadata()
-            print(f'Generated {graph.basename} to {graph.dirname}')
-
-
-def gen_cycle_graph(target_dir, vertices_number):
-    output_graph = rdflib.Graph()
-
-    for i in range(0, vertices_number - 1):
-        add_rdf_edge(i, 'A', i + 1, output_graph)
-
-    add_rdf_edge(vertices_number - 1, 'A', 0, output_graph)
-
-    target = os.path.join(target_dir, f'fullgraph_{vertices_number}')
-
-    write_to_rdf(target, output_graph)
-
-    return f'{target}.xml'
diff --git a/cfpq_data/src/graphs/Graph.py b/cfpq_data/src/graphs/Graph.py
deleted file mode 100644
index c71ef76c..00000000
--- a/cfpq_data/src/graphs/Graph.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from abc import *
-
-
-class Graph(ABC):
-    @abstractmethod
-    def get_metadata(self):
-        pass
-
-    @abstractmethod
-    def save_metadata(self):
-        pass
diff --git a/cfpq_data/src/graphs/MemoryAliases.py b/cfpq_data/src/graphs/MemoryAliases.py
deleted file mode 100644
index e7b4acd1..00000000
--- a/cfpq_data/src/graphs/MemoryAliases.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.graphs.Graph import Graph
-from cfpq_data.src.tools.CmdParser import CmdParser
-from cfpq_data.src.utils import *
-
-
-class MemoryAliases(Graph, CmdParser):
-    graphs = dict()
-    graph_keys = get_info()['MemoryAliases']
-
-    def __init__(self, graph_name):
-        download_data('MemoryAliases', graph_name, MemoryAliases.graph_keys[graph_name])
-        path_to_graph = unpack_graph('MemoryAliases', graph_name)
-
-        self.graph = rdflib.Graph()
-        self.graph.load(path_to_graph)
-
-        self.dirname = os.path.dirname(path_to_graph)
-        self.basename = os.path.basename(path_to_graph)
-
-        self.number_of_vertices = len(self.graph.all_nodes())
-        self.number_of_edges = len(self.graph)
-
-        self.file_size = os.path.getsize(path_to_graph)
-        self.file_name, self.file_extension = os.path.splitext(self.basename)
-
-        MemoryAliases.graphs[self.basename] = path_to_graph
-
-    def get_metadata(self):
-        return {
-            'name': self.basename
-            , 'path': self.dirname
-            , 'version': get_info()['version']
-            , 'vertices': self.number_of_vertices
-            , 'edges': self.number_of_edges
-            , 'size of file': self.file_size
-        }
-
-    def save_metadata(self):
-        with open(f'{self.dirname}/{self.file_name}_meta.json', 'w') as metadata_file:
-            json.dump(self.get_metadata(), metadata_file, indent=4)
-
-    @staticmethod
-    def init_cmd_parser(parser):
-        parser.add_argument(
-            '-a'
-            , '--all'
-            , action='store_true'
-            , help='Load all MemoryAliases graphs from dataset'
-        )
-        parser.add_argument(
-            '-g'
-            , '--graph'
-            , choices=list(get_info()['MemoryAliases'].keys())
-            , required=False
-            , type=str
-            , help='Load specific MemoryAliases graph from dataset'
-        )
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.all is False and args.graph is None:
-            print('One of -a/--all, -g/--graph required')
-            exit()
-
-        if args.all is True:
-            clean_dir('MemoryAliases')
-            for graph_name in tqdm(MemoryAliases.graph_keys, desc='Downloading MemoryAliases'):
-                MemoryAliases(graph_name).save_metadata()
-
-        if args.graph is not None:
-            graph = MemoryAliases(args.graph)
-            graph.save_metadata()
-            print(f'Loaded {graph.basename} to {graph.dirname}')
diff --git a/cfpq_data/src/graphs/RDF.py b/cfpq_data/src/graphs/RDF.py
deleted file mode 100644
index 8a3affe6..00000000
--- a/cfpq_data/src/graphs/RDF.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.graphs.Graph import Graph
-from cfpq_data.src.tools.CmdParser import CmdParser
-from cfpq_data.src.utils import *
-
-
-class RDF(Graph, CmdParser):
-    graphs = dict()
-    graph_keys = get_info()['RDF']
-
-    def __init__(self, graph_name):
-        download_data('RDF', graph_name, RDF.graph_keys[graph_name])
-        path_to_graph = unpack_graph('RDF', graph_name)
-
-        self.graph = rdflib.Graph()
-        self.graph.load(path_to_graph)
-
-        self.dirname = os.path.dirname(path_to_graph)
-        self.basename = os.path.basename(path_to_graph)
-
-        self.number_of_vertices = len(self.graph.all_nodes())
-        self.number_of_edges = len(self.graph)
-
-        self.file_size = os.path.getsize(path_to_graph)
-        self.file_name, self.file_extension = os.path.splitext(self.basename)
-
-        RDF.graphs[self.basename] = path_to_graph
-
-    def get_metadata(self):
-        return {
-            'name': self.basename
-            , 'path': self.dirname
-            , 'version': get_info()['version']
-            , 'vertices': self.number_of_vertices
-            , 'edges': self.number_of_edges
-            , 'size of file': self.file_size
-        }
-
-    def save_metadata(self):
-        with open(f'{self.dirname}/{self.file_name}_meta.json', 'w') as metadata_file:
-            json.dump(self.get_metadata(), metadata_file, indent=4)
-
-    @staticmethod
-    def init_cmd_parser(parser):
-        parser.add_argument(
-            '-a'
-            , '--all'
-            , action='store_true'
-            , help='Load all RDF graphs from dataset'
-        )
-        parser.add_argument(
-            '-g'
-            , '--graph'
-            , choices=list(get_info()['RDF'].keys())
-            , required=False
-            , type=str
-            , help='Load specific RDF graph from dataset'
-        )
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.all is False and args.graph is None:
-            print('One of -a/--all, -g/--graph required')
-            exit()
-
-        if args.all is True:
-            clean_dir('RDF')
-            for graph_name in tqdm(RDF.graph_keys, desc='Downloading RDF'):
-                RDF(graph_name).save_metadata()
-
-        if args.graph is not None:
-            graph = RDF(args.graph)
-            graph.save_metadata()
-            print(f'Loaded {graph.basename} to {graph.dirname}')
diff --git a/cfpq_data/src/graphs/ScaleFree.py b/cfpq_data/src/graphs/ScaleFree.py
deleted file mode 100644
index 67d26b13..00000000
--- a/cfpq_data/src/graphs/ScaleFree.py
+++ /dev/null
@@ -1,126 +0,0 @@
-from itertools import product
-
-import numpy as np
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.graphs.Graph import Graph
-from cfpq_data.src.tools.CmdParser import CmdParser
-from cfpq_data.src.tools.rdf_helper import write_to_rdf, add_rdf_edge
-from cfpq_data.src.utils import *
-
-SCALEFREE_GRAPH_TO_GEN = list(product(
-    [100, 500, 2500, 10000]
-    , [1, 3, 5, 10]
-))
-
-
-class ScaleFree(Graph, CmdParser):
-    graphs = {}
-
-    def __init__(self, vertices_number, vertices_degree):
-        self.dirname = add_graph_dir('ScaleFree')
-        self.basename = f'scale_free_graph_{vertices_number}_{vertices_degree}.xml'
-
-        path_to_graph = gen_scale_free_graph(self.dirname, vertices_number, vertices_degree)
-
-        self.graph = rdflib.Graph()
-        self.graph.load(path_to_graph)
-
-        self.vertices_number = len(self.graph.all_nodes())
-        self.number_of_edges = len(self.graph)
-
-        self.file_size = os.path.getsize(path_to_graph)
-        self.file_name, self.file_extension = os.path.splitext(self.basename)
-
-        ScaleFree.graphs[self.basename] = path_to_graph
-
-    def get_metadata(self):
-        return {
-            'name': self.basename
-            , 'path': self.dirname
-            , 'version': get_info()['version']
-            , 'vertices': self.vertices_number
-            , 'edges': self.number_of_edges
-            , 'size of file': self.file_size
-        }
-
-    def save_metadata(self):
-        with open(f'{self.dirname}/{self.file_name}_meta.json', 'w') as metadata_file:
-            json.dump(self.get_metadata(), metadata_file, indent=4)
-
-    @staticmethod
-    def init_cmd_parser(parser):
-        parser.add_argument(
-            '-p'
-            , '--preset'
-            , action='store_true'
-            , help='Load preset ScaleFree graphs from dataset'
-        )
-        parser.add_argument(
-            '-n'
-            , '--vertices_number'
-            , required=False
-            , type=int
-            , help='Number of vertices of ScaleFree graph'
-        )
-        parser.add_argument(
-            '-k'
-            , '--vertices_degree'
-            , required=False
-            , type=int
-            , help='Degree of vertices in a graph'
-        )
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.preset is False and \
-                (args.vertices_number is None or args.vertices_degree is None):
-            print("One of -p/--preset, (-n/--vertices_number and necessarily -k/--vertices_degree) required")
-            exit()
-
-        if args.preset is True:
-            for n, k in tqdm(SCALEFREE_GRAPH_TO_GEN, desc='ScaleFree graphs generation'):
-                ScaleFree(n, k).save_metadata()
-
-        if args.vertices_number is not None and args.vertices_degree is not None:
-            graph = ScaleFree(args.vertices_number, args.vertices_degree)
-            graph.save_metadata()
-            print(f'Generated {graph.basename} to {graph.dirname}')
-
-
-def gen_scale_free_graph(target_dir, n, k, labels=('a', 'b', 'c', 'd')):
-    g = {
-        i: [(j, np.random.choice(labels)) for j in range(k)]
-        for i in range(k)
-    }
-
-    degree = [3] * k
-
-    for i in range(k, n):
-        to_vertices = np.random.choice(
-            range(i)
-            , size=k
-            , replace=False
-            , p=np.array(degree) / sum(degree)
-        )
-
-        g[i] = []
-        degree.append(0)
-        for to in to_vertices:
-            label = np.random.choice(labels)
-            g[i].append((to, label))
-            degree[to] += 1
-            degree[i] += 1
-
-    output_graph = Graph()
-
-    for v in g:
-        for to in g[v]:
-            add_rdf_edge(v, to[1], to[0], output_graph)
-
-    target = os.path.join(target_dir, f'scale_free_graph_{n}_{k}')
-
-    write_to_rdf(target, output_graph)
-
-    return f'{target}.xml'
diff --git a/cfpq_data/src/graphs/SparseGraph.py b/cfpq_data/src/graphs/SparseGraph.py
deleted file mode 100644
index ec1ad93b..00000000
--- a/cfpq_data/src/graphs/SparseGraph.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import networkx as nx
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.graphs.Graph import Graph
-from cfpq_data.src.tools.CmdParser import CmdParser
-from cfpq_data.src.tools.rdf_helper import write_to_rdf, add_rdf_edge
-from cfpq_data.src.utils import *
-
-SPARSE_GRAPH_TO_GEN = [
-    [5000, 0.001]
-    , [10000, 0.001]
-    , [10000, 0.01]
-    , [10000, 0.1]
-    , [20000, 0.001]
-    , [40000, 0.001]
-    , [80000, 0.001]
-]
-
-
-class SparseGraph(Graph, CmdParser):
-    graphs = {}
-
-    def __init__(self, vertices_number, edge_probability):
-        self.dirname = add_graph_dir('SparseGraph')
-        self.basename = f'G{vertices_number}-{edge_probability}'
-
-        path_to_graph = gen_sparse_graph(self.dirname, vertices_number, edge_probability)
-
-        self.graph = rdflib.Graph()
-        self.graph.load(path_to_graph)
-
-        self.vertices_number = len(self.graph.all_nodes())
-        self.number_of_edges = len(self.graph)
-
-        self.file_size = os.path.getsize(path_to_graph)
-        self.file_name, self.file_extension = os.path.splitext(self.basename)
-
-        SparseGraph.graphs[self.basename] = path_to_graph
-
-    def get_metadata(self):
-        return {
-            'name': self.basename
-            , 'path': self.dirname
-            , 'version': get_info()['version']
-            , 'vertices': self.vertices_number
-            , 'edges': self.number_of_edges
-            , 'size of file': self.file_size
-        }
-
-    def save_metadata(self):
-        with open(f'{self.dirname}/{self.file_name}_meta.json', 'w') as metadata_file:
-            json.dump(self.get_metadata(), metadata_file, indent=4)
-
-    @staticmethod
-    def init_cmd_parser(parser):
-        parser.add_argument(
-            '-p'
-            , '--preset'
-            , action='store_true'
-            , help='Load preset SparseGraph graphs from dataset'
-        )
-        parser.add_argument(
-            '-n'
-            , '--vertices_number'
-            , required=False
-            , type=int
-            , help='Number of vertices of SparseGraph graph'
-        )
-        parser.add_argument(
-            '-pr'
-            , '--edge_probability'
-            , required=False
-            , type=float
-            , help='Probability of edge occurrence in graph'
-        )
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.preset is False and \
-                (args.vertices_number is None or args.edge_probability is None):
-            print("One of -p/--preset, (-n/--vertices_number and necessarily -с/--edge_probability) required")
-            exit()
-
-        if args.preset is True:
-            for g in tqdm(SPARSE_GRAPH_TO_GEN, desc='Sparse graphs generation'):
-                SparseGraph(g[0], g[1]).save_metadata()
-
-        if args.vertices_number is not None and args.edge_probability is not None:
-            graph = SparseGraph(args.vertices_number, args.edge_probability)
-            graph.save_metadata()
-            print(f'Generated {graph.basename} to {graph.dirname}')
-
-
-def gen_sparse_graph(target_dir, vertices_number, edge_probability):
-    tmp_graph = nx.generators.fast_gnp_random_graph(vertices_number, edge_probability)
-
-    output_graph = Graph()
-
-    for v, to in tmp_graph.edges():
-        add_rdf_edge(v, 'A', to, output_graph)
-        add_rdf_edge(to, 'AR', v, output_graph)
-
-    target = os.path.join(target_dir, f'G{vertices_number}-{edge_probability}')
-
-    write_to_rdf(target, output_graph)
-
-    return f'{target}.xml'
diff --git a/cfpq_data/src/graphs/WorstCase.py b/cfpq_data/src/graphs/WorstCase.py
deleted file mode 100644
index e5f391d9..00000000
--- a/cfpq_data/src/graphs/WorstCase.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.graphs.Graph import Graph
-from cfpq_data.src.tools.CmdParser import CmdParser
-from cfpq_data.src.tools.rdf_helper import write_to_rdf, add_rdf_edge
-from cfpq_data.src.utils import *
-
-NUMBER_OF_WORST_CASES = 12
-
-
-class WorstCase(Graph, CmdParser):
-    graphs = {}
-
-    def __init__(self, vertices_number):
-        self.dirname = add_graph_dir('WorstCase')
-        self.basename = f'worstcase_{vertices_number}.xml'
-
-        path_to_graph = gen_worst_case_graph(self.dirname, vertices_number)
-
-        self.graph = rdflib.Graph()
-        self.graph.load(path_to_graph)
-
-        self.vertices_number = len(self.graph.all_nodes())
-        self.number_of_edges = len(self.graph)
-
-        self.file_size = os.path.getsize(path_to_graph)
-        self.file_name, self.file_extension = os.path.splitext(self.basename)
-
-        WorstCase.graphs[self.basename] = path_to_graph
-
-    def get_metadata(self):
-        return {
-            'name': self.basename
-            , 'path': self.dirname
-            , 'version': get_info()['version']
-            , 'vertices': self.vertices_number
-            , 'edges': self.number_of_edges
-            , 'size of file': self.file_size
-        }
-
-    def save_metadata(self):
-        with open(f'{self.dirname}/{self.file_name}_meta.json', 'w') as metadata_file:
-            json.dump(self.get_metadata(), metadata_file, indent=4)
-
-    @staticmethod
-    def init_cmd_parser(parser):
-        parser.add_argument(
-            '-p'
-            , '--preset'
-            , action='store_true'
-            , help='Load preset WorstCase graphs from dataset'
-        )
-        parser.add_argument(
-            '-n'
-            , '--vertices_number'
-            , required=False
-            , type=int
-            , help='Number of vertices of WorstCase graph'
-        )
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.preset is False and args.vertices_number is None:
-            print("One of -p/--preset, -n/--vertices_number required")
-            exit()
-
-        if args.preset is True:
-            for n in tqdm(range(2, NUMBER_OF_WORST_CASES), desc='WorstCase graphs generation'):
-                WorstCase(2 ** n).save_metadata()
-
-        if args.vertices_number is not None:
-            graph = WorstCase(args.vertices_number)
-            graph.save_metadata()
-            print(f'Generated {graph.basename} to {graph.dirname}')
-
-
-def gen_worst_case_graph(target_dir, vertices_number):
-    output_graph = rdflib.Graph()
-
-    first_cycle = int(vertices_number / 2) + 1
-
-    for i in range(0, first_cycle - 1):
-        add_rdf_edge(i, 'A', i + 1, output_graph)
-
-    add_rdf_edge(first_cycle - 1, 'A', 0, output_graph)
-    add_rdf_edge(first_cycle - 1, 'B', first_cycle, output_graph)
-
-    for i in range(first_cycle, vertices_number - 1):
-        add_rdf_edge(i, 'B', i + 1, output_graph)
-
-    add_rdf_edge(vertices_number - 1, 'B', first_cycle - 1, output_graph)
-
-    target = os.path.join(target_dir, f'worstcase_{vertices_number}')
-
-    write_to_rdf(target, output_graph)
-
-    return f'{target}.xml'
diff --git a/cfpq_data/src/graphs/full_graph.py b/cfpq_data/src/graphs/full_graph.py
new file mode 100644
index 00000000..7d610ec0
--- /dev/null
+++ b/cfpq_data/src/graphs/full_graph.py
@@ -0,0 +1,132 @@
+from __future__ import annotations
+
+import sys
+from argparse import ArgumentParser, Namespace
+from pathlib import Path
+from typing import Dict, Tuple
+
+import rdflib
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.utils import add_graph_dir
+
+FULL_GRAPH_TO_GEN = [
+    10, 50, 100, 200, 500,
+    1000, 2000, 5000, 10000, 25000, 50000, 80000
+]
+
+
+class FullGraph(RDF, ICmdParser):
+    """
+    FullGraph — cycle graph, all edges are labeled with the same token
+
+    - graphs: already built graphs
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    config: Dict[str, str] = RELEASE_INFO['Generators_Config']
+
+    @classmethod
+    def build(cls, *args: int) -> FullGraph:
+        """
+        Builds FullGraph instance by number of vertices in the graph
+
+        :param args: only one argument - args[0] - number of vertices in the graph
+        :type args: int
+        :return: FullGraph instance
+        :rtype: FullGraph
+        """
+
+        vertices_number = args[0]
+
+        path_to_graph = gen_cycle_graph(add_graph_dir('FullGraph'), vertices_number)
+
+        graph = FullGraph.load_from_rdf(path_to_graph)
+
+        graph.save_metadata()
+
+        return graph
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initializes command line parser
+
+        :param parser: FullGraph subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-p',
+            '--preset',
+            action='store_true',
+            help='Load preset FullGraph graphs from dataset'
+        )
+        parser.add_argument(
+            '-n',
+            '--vertices_number',
+            required=False,
+            ype=int,
+            help='Number of vertices of FullGraph graph'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluates command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.preset is False and args.vertices_number is None:
+            print("One of -p/--preset, -n/--vertices_number required")
+            sys.exit()
+
+        if args.preset is True:
+            for n in tqdm(FULL_GRAPH_TO_GEN, desc='Full graphs generation'):
+                FullGraph.build(n).save_metadata()
+
+        if args.vertices_number is not None:
+            graph = FullGraph.build(args.vertices_number)
+            graph.save_metadata()
+            print(f'Generated {graph.basename} to {graph.dirname}')
+
+
+def gen_cycle_graph(destination_folder: Path, vertices_number: int) -> Path:
+    """
+    Generates one cycle graph with specified number of vertices
+
+    :param destination_folder: directory to save the graph
+    :type destination_folder: Path
+    :param vertices_number: number of vertices in the graph
+    :type vertices_number: int
+    :return: path to generated graph
+    :rtype: Path
+    """
+
+    output_graph = rdflib.Graph()
+
+    edges = list()
+
+    for i in range(0, vertices_number - 1):
+        edges.append((i, 'A', i + 1))
+
+    edges.append((vertices_number - 1, 'A', 0))
+
+    for subj, pred, obj in tqdm(edges, desc=f'fullgraph_{vertices_number} generation'):
+        add_rdf_edge(subj, pred, obj, output_graph)
+
+    target = destination_folder / f'fullgraph_{vertices_number}.xml'
+
+    write_to_rdf(target, output_graph)
+
+    return target
diff --git a/cfpq_data/src/graphs/graph_interface.py b/cfpq_data/src/graphs/graph_interface.py
new file mode 100644
index 00000000..7b595853
--- /dev/null
+++ b/cfpq_data/src/graphs/graph_interface.py
@@ -0,0 +1,76 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import Union, Dict, Any
+
+
+class IGraph(ABC):
+    """
+    Generic graph interface
+
+    """
+
+    @classmethod
+    @abstractmethod
+    def build(cls, *args: Any) -> IGraph:
+        """
+        Builds graph by arguments
+
+        :param args: various arguments
+        :type args: Any
+        :return: built graph
+        :rtype: IGraph
+        """
+
+    @classmethod
+    @abstractmethod
+    def load(cls,
+             source: Union[Path, str],
+             source_file_format: str = 'rdf') -> IGraph:
+        """
+        Loads graph from specified destination with specified destination_file_format
+
+        :param source: graph destination
+        :type source: Optional[Union[Path, str]]
+        :param source_file_format: graph destination_file_format ('txt'/'rdf')
+        :type source_file_format: str
+        :return: loaded graph
+        :rtype: IGraph
+        """
+
+    @abstractmethod
+    def save(self,
+             destination: Union[Path, str],
+             destination_file_format: str = 'rdf',
+             config: Dict[str, str] = None) -> Path:
+        """
+        Saves graph with specified destination_file_format and edge configuration
+
+        :param destination: path to save the graph
+        :type destination: Optional[Union[Path, str]]
+        :param destination_file_format: graph source_file_format
+        :type destination_file_format: str
+        :param config: edges configuration
+        :type config: Dict[str, str]
+        :return: path to saved graph
+        :rtype: Path
+        """
+
+    @abstractmethod
+    def get_metadata(self) -> Dict[str, str]:
+        """
+        Generates graph metadata
+
+        :return: metadata
+        :rtype: Dict[str, str]
+        """
+
+    @abstractmethod
+    def save_metadata(self) -> Path:
+        """
+        Saves metadata to specified file
+
+        :return: path to file with graph metadata
+        :rtype: Path
+        """
diff --git a/cfpq_data/src/graphs/lubm_graph.py b/cfpq_data/src/graphs/lubm_graph.py
new file mode 100644
index 00000000..86d0a4c9
--- /dev/null
+++ b/cfpq_data/src/graphs/lubm_graph.py
@@ -0,0 +1,187 @@
+from __future__ import annotations
+
+import os
+import shutil
+import subprocess
+from argparse import ArgumentParser, Namespace
+from pathlib import Path
+from typing import Dict, Tuple, Union
+
+import rdflib
+import wget
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO, MAIN_FOLDER
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.utils import add_graph_dir
+
+LUBM_URL = 'http://swat.cse.lehigh.edu/projects/lubm/uba1.7.zip'
+MAX_FILES_PER_UNI = 30
+
+
+class LUBM(RDF, ICmdParser):
+    """
+    LUBM - Lehigh University Benchmark graph
+
+    - graphs: already built graphs
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    config = RELEASE_INFO['LUBM_Config']
+
+    @classmethod
+    def build(cls, *args: Union[int, Tuple[int, int]]) -> LUBM:
+        """
+        Builds LUBM instance by number of generated graphs to create one LUBM graph
+
+        :param args: args[0] - number of generated graphs, args[1] (optional) - edges configuration
+        :type args: Union[int, Tuple[int, int]]
+        :return: LUBM instance
+        :rtype: LUBM
+        """
+
+        count = int(args[0])
+
+        replace = RELEASE_INFO['LUBM_Config']
+        if len(args) > 1:
+            config = args[1]
+            for edge_config in open(config, 'r').readlines():
+                pair = edge_config.split(' ')
+                old = rdflib.URIRef(pair[0].strip(' '))
+                new = pair[1].strip('\n').strip(' ')
+                replace[old] = new
+
+        path_to_graph = gen_lubm_graph(add_graph_dir('LUBM'), count, replace)
+
+        graph = LUBM.load_from_rdf(path_to_graph)
+
+        graph.save_metadata()
+
+        return graph
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initializes command line parser
+
+        :param parser: LUBM subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-n',
+            '--number',
+            required=True,
+            type=int,
+            help='Number of generated graphs to create LUBM graph'
+        )
+
+        parser.add_argument(
+            '-c',
+            '--config',
+            required=False,
+            type=str,
+            help='Path to configuration file'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluates command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.config is not None:
+            graph = LUBM.build(args.count, args.config)
+        else:
+            graph = LUBM.build(args.count)
+        graph.save_metadata()
+        print(f'Generated {graph.basename} to {graph.dirname}')
+
+
+def gen_lubm_graph(destination_folder: Path,
+                   count: int,
+                   config: Dict[str, str]) -> Path:
+    """
+    Generates LUBM graph by specified number of generated graphs to create one LUBM graph
+
+    :param destination_folder: directory to save the graph
+    :type destination_folder: Path
+    :param count: number of generated graphs
+    :type count: int
+    :param config: edges configuration
+    :type config: Dict[str, str]
+    :return: path to generated graph
+    :rtype: Path
+    """
+
+    arch = MAIN_FOLDER / 'data' / 'LUBM' / 'uba.zip'
+    univ_dir = MAIN_FOLDER / 'data' / 'LUBM' / 'univ'
+    if not os.path.exists(univ_dir):
+        wget.download(url=LUBM_URL, out=str(arch))
+        shutil.unpack_archive(filename=str(arch), extract_dir=str(univ_dir))
+        os.remove(arch)
+    subprocess.run(
+        [
+            'java',
+            '-cp',
+            'classes',
+            'edu.lehigh.swat.bench.uba.Generator',
+            '-univ',
+            f'{count}',
+            '-onto',
+            'http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl'
+        ],
+        cwd=str(univ_dir),
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        check=True
+    )
+
+    output_graph = rdflib.Graph()
+
+    triples = list()
+
+    vertices = dict()
+    next_id = 0
+
+    generated_graphs = \
+        os.listdir(MAIN_FOLDER / 'data' / 'LUBM') + \
+        os.listdir(univ_dir)  # for Windows
+
+    for tmp_graph_path in generated_graphs:
+        if 'University' not in tmp_graph_path:
+            continue
+
+        tmp_graph_path = f"{MAIN_FOLDER / 'data' / 'LUBM'}/{tmp_graph_path}"
+        tmp_graph = rdflib.Graph()
+        tmp_graph.parse(tmp_graph_path)
+
+        for subj, pred, obj in tmp_graph:
+            for tmp in [subj, obj]:
+                if tmp not in vertices:
+                    vertices[tmp] = next_id
+                    next_id += 1
+            if pred in config:
+                triples.append((vertices[subj], pred, vertices[obj]))
+            else:
+                triples.append((vertices[subj], 'default', vertices[obj]))
+
+        os.remove(tmp_graph_path)
+
+    for subj, pred, obj in tqdm(triples, desc=f'lubm_{count} generation'):
+        add_rdf_edge(subj, pred, obj, output_graph, config=config, reverse=True)
+
+    target = destination_folder / f'lubm_{count}.xml'
+
+    write_to_rdf(target, output_graph)
+
+    return target
diff --git a/cfpq_data/src/graphs/memory_aliases_graph.py b/cfpq_data/src/graphs/memory_aliases_graph.py
new file mode 100644
index 00000000..06f3a0f6
--- /dev/null
+++ b/cfpq_data/src/graphs/memory_aliases_graph.py
@@ -0,0 +1,76 @@
+import sys
+from argparse import ArgumentParser, Namespace
+from pathlib import Path
+from typing import Dict, Tuple
+
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.utils import clean_dir
+
+
+class MemoryAliases(RDF, ICmdParser):
+    """
+    MemoryAliases — real-world data for points-to analysis of C code
+
+    - graphs: already built graphs
+    - graph_keys: reserved graph names
+    - config: default edge configuration
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    graph_keys: Dict[str, str] = RELEASE_INFO['MemoryAliases']
+    config: Dict[str, str] = RELEASE_INFO['MemoryAliases_Config']
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initializes command line parser
+
+        :param parser: MemoryAliases subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-a',
+            '--all',
+            action='store_true',
+            help='Load all MemoryAliases graphs from dataset'
+        )
+        parser.add_argument(
+            '-g',
+            '--graph',
+            choices=list(RELEASE_INFO['MemoryAliases'].keys()),
+            required=False,
+            type=str,
+            help='Load specific MemoryAliases graph from dataset'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluates command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.all is False and args.graph is None:
+            print('One of -a/--all, -g/--graph required')
+            sys.exit()
+
+        if args.all is True:
+            clean_dir('MemoryAliases')
+            for graph_name in tqdm(MemoryAliases.graph_keys, desc='Downloading MemoryAliases'):
+                MemoryAliases.load_from_rdf(graph_name).save_metadata()
+
+        if args.graph is not None:
+            graph = MemoryAliases.load_from_rdf(args.graph)
+            graph.save_metadata()
+            print(f'Loaded {graph.basename} to {graph.dirname}')
diff --git a/cfpq_data/src/graphs/rdf_graph.py b/cfpq_data/src/graphs/rdf_graph.py
new file mode 100644
index 00000000..9c4c2cc2
--- /dev/null
+++ b/cfpq_data/src/graphs/rdf_graph.py
@@ -0,0 +1,342 @@
+from __future__ import annotations
+
+import json
+import os
+import sys
+from argparse import ArgumentParser, Namespace
+from pathlib import Path
+from typing import Optional, Tuple, Any, List, Dict, Union
+
+import rdflib
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO, MAIN_FOLDER
+from cfpq_data.src.graphs.graph_interface import IGraph
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.rdf_graphs_downloader import download_data
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.utils import unpack_graph, clean_dir
+
+
+class RDF(IGraph, ICmdParser):
+    """
+    RDF — fixed versions of real-world RDF files (links are provided for updating purposes only!)
+
+    - graphs: already built graphs
+    - graph_keys: reserved graph names
+    - config: default edge configuration
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    graph_keys: Dict[str, str] = RELEASE_INFO['RDF']
+    config: Dict[str, str] = RELEASE_INFO['RDF_Config']
+
+    def __init__(self):
+        """
+        Generic constructor
+
+        - type: type of graph instance
+        - store: stored rdflib graph
+        - path: absolute path to graph
+        - dirname: absolute path to graph directory
+        - basename: graph file
+        - vertices_number: number of vertices in the graph
+        - edges_number: number of edges in the graph
+        - file_size: size of graph file, in bytes
+        - file_name: name of graph file
+        - file_extension: extension of graph file
+        """
+
+        self.type: Optional[str] = None
+
+        self.store: Optional[rdflib.Graph] = None
+
+        self.path: Optional[Path] = None
+        self.dirname: Optional[Path] = None
+        self.basename: Optional[str] = None
+
+        self.vertices_number: Optional[int] = None
+        self.edges_number: Optional[int] = None
+
+        self.file_size: Optional[int] = None
+        self.file_name: Optional[str] = None
+        self.file_extension: Optional[str] = None
+
+    @classmethod
+    def build(cls, *args: Union[Path, str]) -> RDF:
+        """
+        An RDF graph builder
+
+        :param args: only one argument - args[0] - path to graph or reserved graph name
+        :type args: Union[Path, str]
+        :return: RDF graph instance
+        :rtype: RDF
+        """
+
+        try:
+            source = args[0]
+            graph = cls.load(args[0])
+            graph.save_metadata()
+            cls.graphs[(graph.basename, graph.file_extension)] = source
+            return graph
+        except BaseException as ex:
+            raise BaseException(f'{cls.__name__}.build: {ex}') from ex
+
+    @classmethod
+    def load(cls,
+             source: Union[Path, str],
+             source_file_format: str = 'rdf') -> RDF:
+        """
+        Loads RDF graph from specified source with specified source_file_format
+
+        :param source: graph source
+        :type source: Optional[Union[Path, str]]
+        :param source_file_format: graph format ('txt'/'rdf')
+        :type source_file_format: str
+        :return: loaded graph
+        :rtype: RDF
+        """
+
+        if source_file_format == 'txt':
+            rdf_graph = cls.load_from_txt(source)
+        else:
+            rdf_graph = cls.load_from_rdf(source)
+
+        return rdf_graph
+
+    def save(self,
+             destination: Union[Path, str],
+             destination_file_format: str = 'rdf',
+             config: Dict[str, str] = None) -> Path:
+        """
+        Saves RDF graph to destination with specified destination_file_format and edge configuration
+
+        :param destination: path to save the graph
+        :type destination: Optional[Union[Path, str]]
+        :param destination_file_format: graph format
+        :type destination_file_format: str
+        :param config: edges configuration
+        :type config: Dict[str, str]
+        :return: path to saved graph
+        :rtype: Path
+        """
+
+        if destination is None:
+            destination = MAIN_FOLDER / 'data' / self.type / 'Graphs' / self.basename
+        if destination_file_format == 'txt':
+            self.save_to_txt(destination, config)
+        else:
+            self.save_to_rdf(destination)
+        return destination
+
+    def get_metadata(self) -> Dict[str, str]:
+        """
+        Generates RDF graph metadata
+
+        :return: metadata
+        :rtype: Dict[str, str]
+        """
+
+        return {
+            'name': str(self.basename),
+            'path': str(self.path),
+            'version': RELEASE_INFO['version'],
+            'vertices': self.vertices_number,
+            'edges': self.edges_number,
+            'size of file': self.file_size
+        }
+
+    def save_metadata(self) -> Path:
+        """
+        Saves metadata to specified file
+
+        :return: path to file with graph metadata
+        :rtype: Path
+        """
+
+        metadata_file_path = self.dirname / f'{self.file_name}_meta.json'
+
+        with open(metadata_file_path, 'w') as metadata_file:
+            json.dump(self.get_metadata(), metadata_file, indent=4)
+
+        return metadata_file_path
+
+    def get_triples(self) -> List[Tuple[Any, Any, Any]]:
+        """
+        Returns edges as list of triples (subject, predicate, object)
+
+        :return: edges
+        :rtype: List[Tuple[Any, Any, Any]]
+        """
+
+        triples = list()
+
+        for subj, pred, obj in self.store:
+            triples.append((subj, pred, obj))
+
+        return triples
+
+    @classmethod
+    def load_from_rdf(cls, source: Path = None) -> RDF:
+        """
+        Loads RDF graph from specified source with rdf format
+
+        :param source: graph source
+        :type source: Path
+        :return: loaded graph
+        :rtype: RDF
+        """
+
+        if hasattr(cls, 'graph_keys') and source in cls.graph_keys:
+            graph_name = source[:]
+            download_data(cls.__name__, graph_name, cls.graph_keys[graph_name])
+            source = unpack_graph(cls.__name__, graph_name)
+
+        graph = cls()
+
+        graph.type = cls.__name__
+
+        graph.store = rdflib.Graph()
+        graph.store.parse(location=str(source), format='xml')
+
+        graph.path = Path(source)
+        graph.dirname = Path(os.path.dirname(source))
+        graph.basename = os.path.basename(source)
+
+        graph.vertices_number = len(graph.store.all_nodes())
+        graph.edges_number = len(graph.store)
+
+        graph.file_size = os.path.getsize(source)
+        graph.file_name, graph.file_extension = os.path.splitext(graph.basename)
+
+        return graph
+
+    @classmethod
+    def load_from_txt(cls, source: Path = None) -> RDF:
+        """
+        Loads RDF graph from specified source with txt format
+
+        :param source: graph source
+        :type source: Path
+        :return: loaded graph
+        :rtype: RDF
+        """
+
+        tmp_graph = rdflib.Graph()
+
+        with open(source, 'r') as input_file:
+            for edge in input_file:
+                s, p, o = edge.split()
+                add_rdf_edge(s, p, o, tmp_graph)
+
+        write_to_rdf(Path('tmp.xml'), tmp_graph)
+
+        graph = cls.load_from_rdf(Path('tmp.xml'))
+
+        os.remove('tmp.xml')
+
+        return graph
+
+    def save_to_rdf(self, destination: Path) -> Path:
+        """
+        Saves RDF graph to destination rdf file
+
+        :param destination: path to save the graph
+        :type destination: Path
+        :return: path to saved graph
+        :rtype: Path
+        """
+
+        write_to_rdf(destination, self.store)
+        return destination
+
+    def save_to_txt(self,
+                    destination: Path,
+                    config: Dict[str, str] = None) -> Path:
+        """
+        Saves RDF graph to destination txt file with specified edge configuration
+
+        :param destination: path to save the graph
+        :type destination: Path
+        :param config: edges configuration
+        :type config: Dict[str, str]
+        :return: path to saved graph
+        :rtype: Path
+        """
+
+        vertices = dict()
+        edges = dict()
+        next_id = 0
+        triples = list()
+
+        for subj, pred, obj in self.store:
+            for tmp in [subj, obj]:
+                if tmp not in vertices:
+                    vertices[tmp] = next_id
+                    next_id += 1
+
+            if config is None:
+                config = self.config
+
+            edges[pred] = pred
+            if pred in config:
+                edges[pred] = config[pred]
+            elif 'default' in config:
+                edges[pred] = config['default']
+
+            triples.append((vertices[subj], edges[pred], vertices[obj]))
+
+        with open(destination, 'w') as output_file:
+            for s, p, o in triples:
+                output_file.write(f'{s} {p} {o}\n')
+
+        return destination
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initialize command line parser
+        :param parser: RDF subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-a',
+            '--all',
+            action='store_true',
+            help='Load all RDF graphs from dataset'
+        )
+        parser.add_argument(
+            '-g',
+            '--graph',
+            choices=list(RELEASE_INFO['RDF'].keys()),
+            required=False,
+            type=str,
+            help='Load specific RDF graph from dataset'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluate command line parser
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.all is False and args.graph is None:
+            print('One of -a/--all, -g/--graph required')
+            sys.exit()
+
+        if args.all is True:
+            clean_dir('RDF')
+            for graph_name in tqdm(RDF.graph_keys, desc='Downloading RDF'):
+                RDF.load_from_rdf(graph_name).save_metadata()
+
+        if args.graph is not None:
+            graph = RDF.load_from_rdf(args.graph)
+            graph.save_metadata()
+            print(f'Loaded {graph.basename} to {graph.dirname}')
diff --git a/cfpq_data/src/graphs/scale_free_graph.py b/cfpq_data/src/graphs/scale_free_graph.py
new file mode 100644
index 00000000..0defb4e9
--- /dev/null
+++ b/cfpq_data/src/graphs/scale_free_graph.py
@@ -0,0 +1,184 @@
+from __future__ import annotations
+
+import sys
+from argparse import ArgumentParser, Namespace
+from itertools import product
+from pathlib import Path
+from typing import Tuple, Dict
+
+import numpy as np
+import rdflib
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.utils import add_graph_dir
+
+SCALE_FREE_GRAPH_TO_GEN = list(product(
+    [100, 500, 2500, 10000],
+    [1, 3, 5, 10]
+))
+
+
+class ScaleFree(RDF, ICmdParser):
+    """
+    ScaleFree — graphs generated by using the Barab'asi-Albert model of scale-free networks
+
+    - graphs: already built graphs
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    config: Dict[str, str] = RELEASE_INFO['Generators_Config']
+
+    @classmethod
+    def build(cls,
+              vertices_number: int,
+              vertices_degree: int) -> ScaleFree:
+        """
+        Builds ScaleFree graph instance by number of vertices and degree of vertex
+
+        :param vertices_number: number of vertices in the graph
+        :type vertices_number: int
+        :param vertices_degree: degree of the vertex in the graph
+        :type vertices_degree: int
+        :return: ScaleFree graph instance
+        :rtype: ScaleFree
+        """
+
+        path_to_graph = gen_scale_free_graph(add_graph_dir('ScaleFree'),
+                                             vertices_number,
+                                             vertices_degree)
+
+        graph = ScaleFree.load_from_rdf(path_to_graph)
+
+        graph.save_metadata()
+
+        return graph
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initialize command line parser
+
+        :param parser: ScaleFree subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-p',
+            '--preset',
+            action='store_true',
+            help='Load preset ScaleFree graphs from dataset'
+        )
+        parser.add_argument(
+            '-n',
+            '--vertices_number',
+            required=False,
+            type=int,
+            help='Number of vertices of ScaleFree graph'
+        )
+        parser.add_argument(
+            '-k',
+            '--vertices_degree',
+            required=False,
+            type=int,
+            help='Degree of vertices in a graph'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluate command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.preset is False and \
+                (args.vertices_number is None or args.vertices_degree is None):
+            print(
+                "One of " +
+                "-p/--preset, " +
+                "(-n/--vertices_number and necessarily -k/--vertices_degree) " +
+                "required"
+            )
+            sys.exit()
+
+        if args.preset is True:
+            for n, k in tqdm(SCALE_FREE_GRAPH_TO_GEN, desc='ScaleFree graphs generation'):
+                ScaleFree.build(n, k).save_metadata()
+
+        if args.vertices_number is not None and args.vertices_degree is not None:
+            graph = ScaleFree.build(args.vertices_number, args.vertices_degree)
+            graph.save_metadata()
+            print(f'Generated {graph.basename} to {graph.dirname}')
+
+
+def gen_scale_free_graph(destination_folder: Path,
+                         vertices_number: int,
+                         vertices_degree: int,
+                         labels: Tuple[str, ...] = ('A', 'B', 'C', 'D')
+                         ) -> Path:
+    """
+    Generates scale free graph
+
+    :param destination_folder: directory to save the graph
+    :type destination_folder: Path
+    :param vertices_number: number of vertices in the graph
+    :type vertices_number: int
+    :param vertices_degree: degree of a vertex in the graph
+    :type vertices_degree: int
+    :param labels: edge labels in the graph
+    :type labels: Tuple[str, ...]
+    :return: path to generated graph
+    :rtype: Path
+    """
+
+    g = {
+        i: [(j, np.random.choice(labels)) for j in range(vertices_degree)]
+        for i in range(vertices_degree)
+    }
+
+    degree = [3] * vertices_degree
+
+    for i in range(vertices_degree, vertices_number):
+        to_vertices = np.random.choice(
+            range(i),
+            size=vertices_degree,
+            replace=False,
+            p=np.array(degree) / sum(degree)
+        )
+
+        g[i] = []
+        degree.append(0)
+        for to in to_vertices:
+            label = np.random.choice(labels)
+            g[i].append((to, label))
+            degree[to] += 1
+            degree[i] += 1
+
+    output_graph = rdflib.Graph()
+
+    edges = list()
+
+    for v in g:
+        for to in g[v]:
+            edges.append((v, to[1], to[0]))
+
+    for subj, pred, obj in tqdm(
+            edges,
+            desc=f'scale_free_graph_{vertices_number}_{vertices_degree} generation'
+    ):
+        add_rdf_edge(subj, pred, obj, output_graph)
+
+    target = destination_folder / f'scale_free_graph_{vertices_number}_{vertices_degree}.xml'
+
+    write_to_rdf(target, output_graph)
+
+    return target
diff --git a/cfpq_data/src/graphs/sparse_graph.py b/cfpq_data/src/graphs/sparse_graph.py
new file mode 100644
index 00000000..bffe23a1
--- /dev/null
+++ b/cfpq_data/src/graphs/sparse_graph.py
@@ -0,0 +1,165 @@
+from __future__ import annotations
+
+import sys
+from argparse import ArgumentParser, Namespace
+from pathlib import Path
+from typing import Dict, Tuple
+
+import networkx as nx
+import rdflib
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.utils import add_graph_dir
+
+SPARSE_GRAPH_TO_GEN = [
+    [5000, 0.001],
+    [10000, 0.001],
+    [10000, 0.01],
+    [10000, 0.1],
+    [20000, 0.001],
+    [40000, 0.001],
+    [80000, 0.001]
+]
+
+
+class SparseGraph(RDF, ICmdParser):
+    """
+    SparseGraph — graphs generated with NetworkX to emulate sparse data
+
+    - graphs: already built graphs
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    config: Dict[str, str] = RELEASE_INFO['Generators_Config']
+
+    @classmethod
+    def build(cls,
+              vertices_number: int,
+              edge_probability: float) -> SparseGraph:
+        """
+        Build SparseGraph instance by number of vertices in probability of edge existence
+
+        :param vertices_number: number of vertices in the graph
+        :type vertices_number: int
+        :param edge_probability: probability of edge existence in the graph
+        :type edge_probability: float
+        :return: SparseGraph instance
+        :rtype: SparseGraph
+        """
+
+        path_to_graph = gen_sparse_graph(
+            add_graph_dir('SparseGraph'),
+            vertices_number,
+            edge_probability
+        )
+
+        graph = SparseGraph.load_from_rdf(path_to_graph)
+
+        graph.save_metadata()
+
+        return graph
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initialize command line parser
+
+        :param parser: SparseGraph subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-p',
+            '--preset',
+            action='store_true',
+            help='Load preset SparseGraph graphs from dataset'
+        )
+        parser.add_argument(
+            '-n',
+            '--vertices_number',
+            required=False,
+            type=int,
+            help='Number of vertices of SparseGraph graph'
+        )
+        parser.add_argument(
+            '-pr',
+            '--edge_probability',
+            required=False,
+            type=float,
+            help='Probability of edge occurrence in graph'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluate command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.preset is False and \
+                (args.vertices_number is None or args.edge_probability is None):
+            print(
+                "One of " +
+                "-p/--preset, " +
+                "(-n/--vertices_number and necessarily -с/--edge_probability) " +
+                "required"
+            )
+            sys.exit()
+
+        if args.preset is True:
+            for g in tqdm(SPARSE_GRAPH_TO_GEN, desc='Sparse graphs generation'):
+                SparseGraph.build(g[0], g[1]).save_metadata()
+
+        if args.vertices_number is not None and args.edge_probability is not None:
+            graph = SparseGraph.build(args.vertices_number, args.edge_probability)
+            graph.save_metadata()
+            print(f'Generated {graph.basename} to {graph.dirname}')
+
+
+def gen_sparse_graph(destination_folder: Path,
+                     vertices_number: int,
+                     edge_probability: float) -> Path:
+    """
+    Generates sparse graph
+
+    :param destination_folder: directory to save the graph
+    :type destination_folder: Path
+    :param vertices_number: number of vertices in the graph
+    :type vertices_number: int
+    :param edge_probability: probability of edge existence in the graph
+    :type edge_probability: float
+    :return: path to generated graph
+    :rtype: Path
+    """
+
+    tmp_graph = nx.generators.fast_gnp_random_graph(vertices_number, edge_probability)
+
+    output_graph = rdflib.Graph()
+
+    edges = list()
+
+    for v, to in tmp_graph.edges():
+        edges.append((v, 'A', to))
+        edges.append((v, 'AR', to))
+
+    for subj, pred, obj in tqdm(
+            edges,
+            desc=f'G{vertices_number}-{edge_probability} generation'
+    ):
+        add_rdf_edge(subj, pred, obj, output_graph)
+
+    target = destination_folder / f'G{vertices_number}-{edge_probability}.xml'
+
+    write_to_rdf(target, output_graph)
+
+    return target
diff --git a/cfpq_data/src/graphs/worst_case_graph.py b/cfpq_data/src/graphs/worst_case_graph.py
new file mode 100644
index 00000000..8f317c63
--- /dev/null
+++ b/cfpq_data/src/graphs/worst_case_graph.py
@@ -0,0 +1,135 @@
+from __future__ import annotations
+
+import sys
+from argparse import ArgumentParser, Namespace
+from pathlib import Path
+from typing import Dict, Tuple
+
+import rdflib
+from tqdm import tqdm
+
+from cfpq_data.config import RELEASE_INFO
+from cfpq_data.src.graphs.rdf_graph import RDF
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.utils import add_graph_dir
+
+NUMBER_OF_WORST_CASES = 12
+
+
+class WorstCase(RDF, ICmdParser):
+    """
+    WorstCase — graphs with two cycles
+
+    - graphs: already built graphs
+    """
+
+    graphs: Dict[Tuple[str, str], Path] = dict()
+    config: Dict[str, str] = RELEASE_INFO['Generators_Config']
+
+    @classmethod
+    def build(cls, vertices_number: int) -> WorstCase:
+        """
+        Builds WorstCase graph instance by number of vertices in the graph
+
+        :param vertices_number: number of vertices in the graph
+        :type vertices_number: int
+        :return: WorstCase graph instance
+        :rtype:WorstCase
+        """
+
+        path_to_graph = gen_worst_case_graph(add_graph_dir('WorstCase'), vertices_number)
+
+        graph = WorstCase.load_from_rdf(path_to_graph)
+
+        graph.save_metadata()
+
+        return graph
+
+    @staticmethod
+    def init_cmd_parser(parser: ArgumentParser) -> None:
+        """
+        Initialize command line parser
+
+        :param parser: WorstCase subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.add_argument(
+            '-p',
+            '--preset',
+            action='store_true',
+            help='Load preset WorstCase graphs from dataset'
+        )
+        parser.add_argument(
+            '-n',
+            '--vertices_number',
+            required=False,
+            type=int,
+            help='Number of vertices of WorstCase graph'
+        )
+
+    @staticmethod
+    def eval_cmd_parser(args: Namespace) -> None:
+        """
+        Evaluate command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
+
+        if args.preset is False and args.vertices_number is None:
+            print("One of -p/--preset, -n/--vertices_number required")
+            sys.exit()
+
+        if args.preset is True:
+            for n in tqdm(range(2, NUMBER_OF_WORST_CASES), desc='WorstCase graphs generation'):
+                WorstCase.build(2 ** n).save_metadata()
+
+        if args.vertices_number is not None:
+            graph = WorstCase.build(args.vertices_number)
+            graph.save_metadata()
+            print(f'Generated {graph.basename} to {graph.dirname}')
+
+
+def gen_worst_case_graph(destination_folder: Path, vertices_number: int) -> Path:
+    """
+    Generates graphs with two cycles by number of vertices in the graph
+
+    :param destination_folder: directory to save the graph
+    :type destination_folder: Path
+    :param vertices_number: number of vertices in the graph
+    :type vertices_number: int
+    :return: path to generated graph
+    :rtype: Path
+    """
+
+    output_graph = rdflib.Graph()
+
+    first_cycle = int(vertices_number / 2) + 1
+
+    edges = list()
+
+    for i in range(0, first_cycle - 1):
+        edges.append((i, 'A', i + 1))
+
+    edges.append((first_cycle - 1, 'A', 0))
+    edges.append((first_cycle - 1, 'B', first_cycle))
+
+    for i in range(first_cycle, vertices_number - 1):
+        edges.append((i, 'B', i + 1))
+
+    edges.append((vertices_number - 1, 'B', first_cycle - 1))
+
+    for subj, pred, obj in tqdm(edges, desc=f'worstcase_{vertices_number} generation'):
+        add_rdf_edge(subj, pred, obj, output_graph)
+
+    target = destination_folder / f'worstcase_{vertices_number}.xml'
+
+    write_to_rdf(target, output_graph)
+
+    return target
diff --git a/cfpq_data/src/tools/CmdParser.py b/cfpq_data/src/tools/CmdParser.py
deleted file mode 100644
index a6936933..00000000
--- a/cfpq_data/src/tools/CmdParser.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import argparse
-from abc import *
-
-
-class CmdParser(ABC):
-    @staticmethod
-    @abstractmethod
-    def init_cmd_parser(parser: argparse.ArgumentParser):
-        parser.parse_args()
-
-    @staticmethod
-    @abstractmethod
-    def eval_cmd_parser(args: argparse.Namespace):
-        pass
diff --git a/cfpq_data/src/tools/LUBM/README.md b/cfpq_data/src/tools/LUBM/README.md
deleted file mode 100644
index cb48fe4f..00000000
--- a/cfpq_data/src/tools/LUBM/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# LUBM dataset
-
-## Folder structure
-- config.txt - configures mappings from IRI names to short string names (for our purposes)
-- download.sh - downloads and unzips the lubm generator from oficial website
-- generate.sh - generates dataset for 10 universities (this could be changed in the script: set -univ N)
-
-
-## Usage
-You can prepare files names of the generated dataset (optional) by running
-```
-$ python3 main.py LUBM prepare --pref PREFIX --new NEW_PREFIX
-```
-Utility downloads lubm java based generating tool and run dataset generating for 10 universities by itself, and then you can run
-generated database file with mapping for merged 3 universities (indicies 0..2) as an example.
-
-```
-$ python3 main.py LUBM convert --pref univ\\University --count 3 --conf config.txt
-```
diff --git a/cfpq_data/src/tools/LUBM/config.txt b/cfpq_data/src/tools/LUBM/config.txt
deleted file mode 100644
index fe290f9c..00000000
--- a/cfpq_data/src/tools/LUBM/config.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#subOrganizationOf SO
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#worksFor WF
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#headOf HF
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#teacherOf TF
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#memberOf MF
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#advisor AD
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#takesCourse TC
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#publicationAuthor PA
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#undergraduateDegreeFrom UG
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#mastersDegreeFrom MD
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#doctoralDegreeFrom DD
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#emailAddress EA
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#name NM
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#researchInterest RI
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#telephone TP
-http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#teachingAssistantOf TA
\ No newline at end of file
diff --git a/cfpq_data/src/tools/LUBM/converter.py b/cfpq_data/src/tools/LUBM/converter.py
deleted file mode 100644
index 5f3bd763..00000000
--- a/cfpq_data/src/tools/LUBM/converter.py
+++ /dev/null
@@ -1,127 +0,0 @@
-"""
-    RDF merger and converter for LUBM dataset.
-    Database is stored in files <prefix><id>_<sub_id>.owl
-    This files are merged for specified number of universities (ids range),
-    and edges are replaced with specified mapping.
-    Also vertices labels also replaces with integer based names.
-
-    Usage:
-    - Create a conversion configuration file. Each line must contain an IRI,
-    a whitespace character and a string to replace the IRI by.
-    - Run main.py LUBM convert --pref PREFIX --count COUNT --conf CONFIG
-    - Result will have name <prefix><count><vertices count><indices count>.xml
-
-    The graph will contain explicit inverted edges added an 'R'.
-    """
-
-import os
-import subprocess
-
-import rdflib
-
-from cfpq_data.src.tools.CmdParser import CmdParser
-
-URI_PREFIX = 'http://yacc/'
-MAX_FILES_PER_UNI = 30
-
-
-# RDF serialization
-def write_to_rdf(target, graph):
-    graph.serialize(target + '.xml', format='xml')
-
-
-# Edge addition (grapf constructing)
-def add_rdf_edge(subj, pred, obj, graph):
-    s = rdflib.BNode('id-%s' % (subj))
-    p = rdflib.URIRef(URI_PREFIX + pred)
-    o = rdflib.BNode('id-%s' % (obj))
-    graph.add((s, p, o))
-
-
-class LUBMGraph(CmdParser):
-    @staticmethod
-    def init_cmd_parser(parser):
-        subparsers = parser.add_subparsers(required=True, dest='mode')
-        prepare_parser = subparsers.add_parser('prepare')
-        converter_parser = subparsers.add_parser('convert')
-
-        prepare_parser.add_argument('--pref', required=True)
-        prepare_parser.add_argument('--new', required=True)
-
-        converter_parser.add_argument('--pref', required=True)
-        converter_parser.add_argument('--count', required=True, type=int)
-        converter_parser.add_argument('--conf', required=True)
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.mode == 'prepare':
-            prefix = args.pref
-            new = args.new
-            files = os.listdir()
-
-            for f in files:
-                if f.startswith(prefix):
-                    name = f.replace(prefix, new)
-                    try:
-                        os.rename(f, name)
-                    except Exception:
-                        print('Failed to rename file: ' + f + ' to ' + name)
-        elif args.mode == 'convert':
-            subprocess.run('bash src/tools/LUBM/download.sh')
-            subprocess.run('bash src/tools/LUBM/generate.sh')
-
-            replace = {}  # map for replacing predicates
-            config = args.conf
-            for l in open(config, 'r').readlines():
-                pair = l.split(' ')
-                old = rdflib.URIRef(pair[0].strip(' '))
-                new = pair[1].strip('\n').strip(' ')
-                replace[old] = new
-
-            print(replace)
-
-            res = {}  # map from resources to integer ids
-            next_id = 0  # id counter
-            edges_count = 0  # Total edges
-
-            graph = rdflib.Graph()
-            prefix = args.pref
-            count = args.count
-
-            processed = []
-            notreplaced = set()
-
-            for i in range(0, count):
-                for j in range(0, MAX_FILES_PER_UNI):
-                    filename = prefix + str(i) + '_' + str(j) + '.owl'
-                    try:
-                        g = rdflib.Graph()
-                        g.parse(filename)
-
-                        for s, p, o in g:
-                            for r in [s, o]:
-                                if r not in res:
-                                    res[r] = str(next_id)
-                                    next_id += 1
-
-                            if p in replace:
-                                add_rdf_edge(res[s], replace[p], res[o], graph)
-                                add_rdf_edge(res[s], replace[p] + 'R', res[o], graph)
-                                edges_count += 2
-                            else:
-                                add_rdf_edge(res[s], 'OTHER', res[o], graph)
-                                edges_count += 1
-                                notreplaced.add(p)
-
-                        processed.append(filename)
-                        print('Merged:', filename)
-                    except Exception:
-                        pass
-
-            target = prefix + str(count) + 'v' + str(next_id) + 'e' + str(edges_count)  # output file
-            write_to_rdf(target, graph)
-
-            print('Total vertices:', next_id)
-            print('Total edges:', edges_count)
-            print('Processed files:\n', processed)
-            print('Not replaced labels:', notreplaced)
diff --git a/cfpq_data/src/tools/LUBM/download.sh b/cfpq_data/src/tools/LUBM/download.sh
deleted file mode 100644
index c60235b8..00000000
--- a/cfpq_data/src/tools/LUBM/download.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-wget http://swat.cse.lehigh.edu/projects/lubm/uba1.7.zip
-unzip uba1.7.zip -d univ
diff --git a/cfpq_data/src/tools/LUBM/generate.sh b/cfpq_data/src/tools/LUBM/generate.sh
deleted file mode 100644
index d7f60676..00000000
--- a/cfpq_data/src/tools/LUBM/generate.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-cd univ
-java -cp classes edu.lehigh.swat.bench.uba.Generator -univ 10 -onto http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl
-cd ..
diff --git a/cfpq_data/src/tools/data_wrapper.py b/cfpq_data/src/tools/data_wrapper.py
deleted file mode 100644
index c9b88d7e..00000000
--- a/cfpq_data/src/tools/data_wrapper.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from cfpq_data.src.tools.filters import *
-
-DATA_DIR = 'data'
-MATRICES = 'Matrices'
-GRAMMARS = 'Grammars'
-
-
-def get_matrix_dir(suite, data_path=DATA_DIR):
-    return os.path.join(data_path, suite, MATRICES)
-
-
-def get_grammar_dir(suite, data_path=DATA_DIR):
-    return os.path.join(data_path, suite, GRAMMARS)
-
-
-class DataWrapper:
-    def __init__(self, data_path=DATA_DIR):
-        self.data_path = data_path
-
-    def get_suites(self):
-        return os.listdir(self.data_path)
-
-    def get_graphs(self, suite,
-                   include_extensions=None, exclude_extensions=None,
-                   max_file_size=None, min_file_size=None,
-                   max_file_len=None, min_file_len=None,
-                   ignore_hidden_files=True):
-        matrix_dir = get_matrix_dir(suite, self.data_path)
-        graph_filter = all_filter_combinator(
-            lambda g: not file_is_hidden(g) if ignore_hidden_files else True,
-            lambda g: file_has_extension(g, include_extensions),
-            lambda g: exclude_extensions is None or not file_has_extension(g, exclude_extensions),
-            lambda g: file_has_size(os.path.join(matrix_dir, g), min_file_size, max_file_size),
-            lambda g: file_has_len(os.path.join(matrix_dir, g), min_file_len, max_file_len),
-        )
-        graphs = os.listdir(matrix_dir) if os.path.exists(matrix_dir) else []
-        return [os.path.join(matrix_dir, graph) for graph in graphs
-                if graph_filter(graph)]
-
-    def get_grammars(self, suite, include_extension=None):
-        grammar_dir = get_grammar_dir(suite, self.data_path)
-        grammar_filter = all_filter_combinator(
-            lambda gr: file_has_extension(gr, include_extension)
-        )
-        grammars = os.listdir(grammar_dir) if os.path.exists(grammar_dir) else []
-        return [os.path.join(grammar_dir, grammar) for grammar in grammars
-                if grammar_filter(grammar)]
diff --git a/cfpq_data/src/tools/filters.py b/cfpq_data/src/tools/filters.py
deleted file mode 100644
index 6c58dcd9..00000000
--- a/cfpq_data/src/tools/filters.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import os
-from functools import reduce
-
-
-def get_file_len(file):
-    with open(file, 'r') as f:
-        return len(f.readlines())
-
-
-def any_filter_combinator(*filters):
-    return reduce(lambda acc, f: lambda x: acc(x) or f(x), filters, lambda _: False)
-
-
-def all_filter_combinator(*filters):
-    return reduce(lambda acc, f: lambda x: acc(x) and f(x), filters, lambda _: True)
-
-
-def file_has_extension(file, extensions=None):
-    return extensions is None or any([file.endswith(ext) for ext in extensions])
-
-
-def file_has_size(file, min_size=None, max_size=None):
-    size = os.path.getsize(file) // 10 ** 6
-    grater_min = min_size is None or min_size < size
-    less_max = max_size is None or size < max_size
-    return grater_min and less_max
-
-
-def file_has_len(file, min_length=None, max_length=None):
-    length = get_file_len(file)
-    grater_min = min_length is None or min_length < length
-    less_max = max_length is None or length < max_length
-    return grater_min and less_max
-
-
-def file_is_hidden(file: str):
-    return file.startswith('.')
diff --git a/cfpq_data/src/tools/gen_RPQ/gen.py b/cfpq_data/src/tools/gen_RPQ/gen.py
index 284ca008..e1606067 100644
--- a/cfpq_data/src/tools/gen_RPQ/gen.py
+++ b/cfpq_data/src/tools/gen_RPQ/gen.py
@@ -4,8 +4,8 @@
 import numpy
 import rdflib
 
-from cfpq_data.src.tools.CmdParser import CmdParser
 from cfpq_data.src.tools.gen_RPQ.RDF_edge_stat import *
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
 
 templates = [(1, '%s*', 'q1'), (2, '%s %s*', 'q2'), (3, '%s %s* %s*', 'q3'),
              (2, '(%s | %s)*', 'q4_2'), (3, '(%s | %s | %s)*', 'q4_3'), (4, '(%s | %s | %s | %s)*', 'q4_4'),
@@ -47,7 +47,7 @@ def print_qs(qs, root_dir):
                 i = i + 1
 
 
-class GenRPQGraph(CmdParser):
+class GenRPQGraph(ICmdParser):
     @staticmethod
     def init_cmd_parser(parser):
         subparsers = parser.add_subparsers(required=True, dest='mode')
diff --git a/cfpq_data/src/tools/grammar2cnf/README.md b/cfpq_data/src/tools/grammar2cnf/README.md
deleted file mode 100644
index a4451a84..00000000
--- a/cfpq_data/src/tools/grammar2cnf/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-Loads context-free grammar and outputs it in CNF.
-
-Use the following input format:
-
-- Line 0:
-
-    ```
-    a set of variable symbols delimited by spaces,
-    the first one is the starting symbol
-    ```
-
-- Line 1:
-
-    ```
-    a set of terminal symbols delimited by spaces
-    ```
-
-- The rest of the lines are productions in the form:
-    
-    ```head -> body | body | ... | body```
-
-    where each body can contain basic regular expression, allowed operators:
-    
-    - The concatenation, the default operator, which can by represented either by a space or a dot (.)
-    
-    - The union, represented by ```|``` 
-
-    - The ```?``` quantifier
-    
-    - The kleene star, represented by ```*```
-    
-    Epsilon symbol should be represented by ```eps```
-
-
-To start run:
-```
-python3 main.py grammar2cnf file [PATH_TO_GRAMMAR] --output [PATH_TO_OUTPUT]
-```
diff --git a/cfpq_data/src/tools/grammar2cnf/examples/example.txt b/cfpq_data/src/tools/grammar2cnf/examples/example.txt
deleted file mode 100644
index 11186ebd..00000000
--- a/cfpq_data/src/tools/grammar2cnf/examples/example.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-S
-a b
-S -> a S b S
-S -> eps
\ No newline at end of file
diff --git a/cfpq_data/src/tools/grammar2cnf/examples/example1.txt b/cfpq_data/src/tools/grammar2cnf/examples/example1.txt
deleted file mode 100644
index 50de16fd..00000000
--- a/cfpq_data/src/tools/grammar2cnf/examples/example1.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-S
-a b
-S -> a S b S | eps
\ No newline at end of file
diff --git a/cfpq_data/src/tools/grammar2cnf/examples/example2.txt b/cfpq_data/src/tools/grammar2cnf/examples/example2.txt
deleted file mode 100644
index 5907e0b6..00000000
--- a/cfpq_data/src/tools/grammar2cnf/examples/example2.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-S
-a b
-S -> (a S b)*
\ No newline at end of file
diff --git a/cfpq_data/src/tools/grammar2cnf/grammar2cnf.py b/cfpq_data/src/tools/grammar2cnf/grammar2cnf.py
deleted file mode 100644
index 50672b9c..00000000
--- a/cfpq_data/src/tools/grammar2cnf/grammar2cnf.py
+++ /dev/null
@@ -1,176 +0,0 @@
-import os
-
-from pyformlang.cfg import Production, Variable, Terminal, CFG, Epsilon
-from pyformlang.regular_expression import Regex
-
-from cfpq_data.src.tools.data_wrapper import DataWrapper
-from cfpq_data.src.tools.CmdParser import CmdParser
-
-EPS_SYM = 'eps'
-SUPPORTED_REGEX_CHARS = '.*()?|'
-
-
-# Counter function used to create unique variable names
-def get_new_var_num():
-    get_new_var_num.calls += 1
-    return get_new_var_num.calls
-
-
-get_new_var_num.calls = 0
-
-
-def regex_to_grammar_productions(regex, head, var_dict, terminal_dict):
-    _var_dict = {}
-    production_set = set()
-
-    # Getting an NFA from regex
-    enfa = regex.to_epsilon_nfa()
-    enfa = enfa.minimize()
-    transitions = enfa._transition_function._transitions
-
-    # Producing variables from NFA states
-    for state in enfa.states:
-        _var_dict[state] = Variable(
-            # Creating new CFG variable with unique name
-            '%s#REGEX#%s' % (head.value, get_new_var_num())
-        )
-
-    for head_state in transitions:
-        # Adding productions from head to start states
-        for start_state in enfa.start_states:
-            start_p = Production(head, [_var_dict[start_state]])
-            production_set.add(start_p)
-
-        # Getting productions from NFA transitions
-        for sym in list(transitions[head_state]):
-            body_state = transitions[head_state][sym]
-            inner_head = _var_dict[head_state]
-            inner_body = []
-
-            if sym in var_dict:
-                inner_body.append(var_dict[sym])
-            elif sym in terminal_dict:
-                inner_body.append(terminal_dict[sym])
-            elif sym == EPS_SYM:
-                inner_body.append(Epsilon())
-            else:
-                raise ValueError(f'''Symbol "{sym}" is not defined as
-                                 a terminal or a variable''')
-
-            inner_body.append(_var_dict[body_state])
-            production_set.add(
-                Production(inner_head, inner_body)
-            )
-
-            if transitions[head_state][sym] in enfa.final_states:
-                eps_p = Production(_var_dict[body_state], [])
-                production_set.add(eps_p)
-    return production_set
-
-
-def from_txt(lines):
-    var_dict = {}
-    terminal_dict = {}
-    production_set = set()
-
-    # variables
-    var_lst = lines[0].split()
-    start_var = Variable(var_lst[0])
-    for v in var_lst:
-        var_dict[v] = Variable(v)
-
-    # terminals
-    for t in lines[1].split():
-        terminal_dict[t] = Terminal(t)
-
-    # productions
-    for l in lines[2:]:
-        pr = l.split(' -> ')
-        head = var_dict[pr[0]]
-        body_str = pr[1].rstrip('\n')
-
-        # pyformlang doesn't accept '?' quantifier, transforming to alternative expression
-        body_str = body_str.replace('?', f'|{EPS_SYM}')
-
-        production_set |= regex_to_grammar_productions(
-            Regex(body_str),
-            head,
-            var_dict,
-            terminal_dict
-        )
-
-    cfg = CFG(
-        start_symbol=start_var, productions=production_set
-    )
-
-    print('Context-free grammar loaded.')
-    # DEBUG OUTPUT
-    # for w in list(cfg.get_words(5)):
-    #     print(w)
-    # print(cfg.start_symbol)
-    # print(f'Variables: {cfg.variables}')
-    # print(f'Terminals: {cfg.terminals}')
-    # print(f'Productions: {cfg.productions}')
-
-    return cfg
-
-
-def to_txt(cfg):
-    variables, productions, terminals = '', '', ''
-
-    for var in cfg.variables:
-        if var != cfg.start_symbol:
-            variables += f'{var.value} '
-    for ter in cfg.terminals:
-        terminals += f'{ter.value} '
-    for pr in cfg.productions:
-        head = pr.head.value
-        productions += (f'{head} -> ')
-        for sym in pr.body:
-            productions += f'{sym.value} '
-        productions += '\n'
-    return f'{cfg.start_symbol} {variables}\n{terminals}\n{productions}'
-
-
-def convert(input, output):
-    with open(input, 'r') as input_file:
-        cfg = from_txt(input_file.readlines())
-        cfg = cfg.to_normal_form()
-        with open(output, 'w') as output_file:
-            output_file.write(to_txt(cfg))
-            print(f'Grammar in CNF written to {output}')
-
-
-def conver_suites(suites):
-    data = DataWrapper()
-    for suite in suites:
-        for grammar in data.get_grammars(suite, include_extension='txt'):
-            convert(grammar, f'{os.path.splitext(grammar)[0]}.cnf')
-
-
-class Grammar2Cnf(CmdParser):
-    @staticmethod
-    def init_cmd_parser(parser):
-        subparsers = parser.add_subparsers(required=True, dest='mode')
-        file_parser = subparsers.add_parser('file')
-        set_parser = subparsers.add_parser('set')
-        subparsers.add_parser('all')
-
-        file_parser.add_argument('path')
-        file_parser.add_argument('--output', help='file path to store result')
-
-        set_parser.add_argument('suite', nargs='*', choices=DataWrapper().get_suites())
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        if args.mode == 'file':
-            inp = args.path
-            if args.output is None:
-                output = f'{os.path.splitext(inp)[0]}.cnf'
-            else:
-                output = args.output
-            convert(inp, output)
-        elif args.mode == 'set':
-            conver_suites(args.suite)
-        elif args.mode == 'all':
-            conver_suites(DataWrapper().get_suites())
diff --git a/cfpq_data/src/tools/graph2txt/converter.conf b/cfpq_data/src/tools/graph2txt/converter.conf
deleted file mode 100644
index 9f8dbc40..00000000
--- a/cfpq_data/src/tools/graph2txt/converter.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-[RDF]
-http://www.w3.org/2000/01/rdf-schema#subClassOf = SCO
-http://www.w3.org/1999/02/22-rdf-syntax-ns#type = T
-
-[WorstCase]
-http://yacc/A = A
-http://yacc/B = B
-
diff --git a/cfpq_data/src/tools/graph2txt/cunstom_example.conf b/cfpq_data/src/tools/graph2txt/cunstom_example.conf
deleted file mode 100644
index 0c343376..00000000
--- a/cfpq_data/src/tools/graph2txt/cunstom_example.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[DEFAULT]
-http://yacc/A = A
-http://yacc/B = B
diff --git a/cfpq_data/src/tools/graph2txt/graph2txt.py b/cfpq_data/src/tools/graph2txt/graph2txt.py
deleted file mode 100644
index cf89de4d..00000000
--- a/cfpq_data/src/tools/graph2txt/graph2txt.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import configparser
-
-import rdflib
-from tqdm import tqdm
-
-from cfpq_data.src.tools.data_wrapper import *
-from cfpq_data.src.tools.CmdParser import CmdParser
-
-GRAPH2TXT_PATH = 'src/tools/graph2txt'
-CONVERTER_CONF_PATH = os.path.join(GRAPH2TXT_PATH, 'converter.conf')
-DEFAULT_SECTION = 'DEFAULT'
-
-
-def convert(path, replace=None, reverse_edges=False):
-    res = {}
-    next_id = 0
-
-    graph_from = rdflib.Graph()
-    graph_from.parse(path)
-
-    graph_to = f'{os.path.splitext(path)[0]}.txt'
-
-    with open(graph_to, 'w') as out:
-        for s, p, o in tqdm(graph_from, desc=f'Converting {graph_from} to {graph_to}'):
-            for r in [s, o]:
-                if r not in res:
-                    res[r] = str(next_id)
-                    next_id += 1
-
-            v, e, to = res[s], p, res[o]
-            if replace:
-                if e in replace:
-                    e = replace[p]
-                    if reverse_edges:
-                        out.write(f'{to} {e}_R {v}\n')
-                else:
-                    e = 'OTHER'
-
-            out.write(f'{v} {e} {to}\n')
-
-
-class Graph2TxtGraph(CmdParser):
-    @staticmethod
-    def init_cmd_parser(parser):
-        subparsers = parser.add_subparsers(required=True, dest='mode')
-        file_parser = subparsers.add_parser('file')
-        set_parser = subparsers.add_parser('set')
-
-        file_parser.add_argument('path')
-        file_parser.add_argument('--conf')
-
-        set_parser.add_argument('suite', nargs='*', choices=DataWrapper().get_suites())
-
-    @staticmethod
-    def eval_cmd_parser(args):
-        config = configparser.ConfigParser(delimiters=('=',))
-
-        if args.mode == 'set':
-            data = DataWrapper()
-            config.read(CONVERTER_CONF_PATH)
-            for suite in args.suite:
-                replacing = config[suite]
-                for graph in data.get_graphs(suite, exclude_extensions=['txt'], max_file_size=1000):
-                    convert(graph, replace=replacing, reverse_edges=True)
-        elif args.mode == 'file':
-            if args.conf:
-                config.read(args.conf)
-                replacing = config[DEFAULT_SECTION]
-            else:
-                replacing = None
-            convert(args.path, replace=replacing, reverse_edges=True)
diff --git a/cfpq_data/src/tools/graspan_to_rdf.py b/cfpq_data/src/tools/graspan_to_rdf.py
index 03220e68..4f75e0fa 100644
--- a/cfpq_data/src/tools/graspan_to_rdf.py
+++ b/cfpq_data/src/tools/graspan_to_rdf.py
@@ -3,7 +3,7 @@
 
 from rdflib import Graph
 
-from cfpq_data.src.tools.rdf_helper import write_to_rdf, add_rdf_edge
+from cfpq_data.src.utils.rdf_helper import write_to_rdf, add_rdf_edge
 
 
 def convert_file(in_file_path, out_file_path):
diff --git a/cfpq_data/src/tools/rdf_helper.py b/cfpq_data/src/tools/rdf_helper.py
deleted file mode 100644
index f88361fc..00000000
--- a/cfpq_data/src/tools/rdf_helper.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from rdflib import URIRef, BNode
-
-URI_PREFIX = 'http://yacc/'
-
-
-# RDF serialization
-def write_to_rdf(target, graph):
-    graph.serialize(target + '.xml', format='xml')
-
-
-def add_rdf_edge(subj, pred, obj, graph):
-    s = BNode('id-%s' % (subj))
-    p = URIRef(URI_PREFIX + pred)
-    o = BNode('id-%s' % (obj))
-    graph.add((s, p, o))
diff --git a/cfpq_data/src/tools/redis_rdf/main.py b/cfpq_data/src/tools/redis_rdf/main.py
index 52b1a605..8390315c 100755
--- a/cfpq_data/src/tools/redis_rdf/main.py
+++ b/cfpq_data/src/tools/redis_rdf/main.py
@@ -2,11 +2,11 @@
 
 import os
 
-from cfpq_data.src.tools.CmdParser import CmdParser
 from cfpq_data.src.tools.redis_rdf.src.redis_loader.loader import load
+from cfpq_data.src.utils.cmd_parser_interface import ICmdParser
 
 
-class RedisRDFGraph(CmdParser):
+class RedisRDFGraph(ICmdParser):
     @staticmethod
     def init_cmd_parser(parser):
         parser.add_argument('--host', help='redis host name', default='localhost')
diff --git a/cfpq_data/src/tools/sierpinski_graph.py b/cfpq_data/src/tools/sierpinski_graph.py
new file mode 100644
index 00000000..c26d9446
--- /dev/null
+++ b/cfpq_data/src/tools/sierpinski_graph.py
@@ -0,0 +1,40 @@
+def gen_sierpinski_graph(target_dir, degree, predicates=('A')):
+    """ Generates a Sierpinski Triangle graph. """
+
+    def sierpinski(t, l, r, deg, preds, g):
+        """ Core function for generating the Sierpinski Triangle. """
+
+        if deg > 0:
+            lt = next(ids)
+            tr = next(ids)
+            rl = next(ids)
+            sierpinski(l, lt, rl, deg - 1, preds, g)
+            sierpinski(lt, t, tr, deg - 1, preds, g)
+            sierpinski(rl, tr, r, deg - 1, preds, g)
+        else:
+            add_edges(l, t, preds, g)
+            add_edges(t, r, preds, g)
+            add_edges(r, l, preds, g)
+
+    def add_edges(u, v, preds, g):
+        """ Adds edges between vertices u and v for all predicates. """
+
+        for p in preds:
+            g += [[u, p, v]]
+            g += [[v, p, u]]
+
+    def _idgen():
+        """ Generates integer identifiers for vertices. """
+
+        c = 4
+        while True:
+            yield c
+            c += 1
+
+    ids = _idgen()
+    graph = []
+    sierpinski(1, 2, 3, degree, predicates, graph)
+
+    with open(target_dir / f'sierpinskigraph_{degree}.txt', 'w') as out_file:
+        for triple in graph:
+            out_file.write(f'{triple[0]} {triple[1]} {triple[2]} \n')
diff --git a/cfpq_data/src/utils.py b/cfpq_data/src/utils.py
deleted file mode 100644
index 8fdcc450..00000000
--- a/cfpq_data/src/utils.py
+++ /dev/null
@@ -1,144 +0,0 @@
-import json
-import os
-import shutil
-from pathlib import Path
-
-import requests
-
-DATA_ROOT_DIR = 'data/'
-GRAPHS_DIR = 'Graphs'
-
-
-def get_info():
-    """ Gets release info from release notes """
-
-    with open(f'{Path(__file__).parent.parent.absolute()}/release_notes.json', 'r') as input_file:
-        data = json.load(input_file)
-        return data
-
-
-def download_file_from_google_drive(id, destination):
-    URL = 'https://docs.google.com/uc?export=download'
-
-    session = requests.Session()
-
-    response = session.get(URL, params={'id': id}, stream=True)
-    token = get_confirm_token(response)
-
-    if token:
-        params = {'id': id, 'confirm': token}
-        response = session.get(URL, params=params, stream=True)
-
-    save_response_content(response, destination)
-
-
-def get_confirm_token(response):
-    for key, value in response.cookies.items():
-        if key.startswith('download_warning'):
-            return value
-
-    return None
-
-
-def save_response_content(response, destination):
-    CHUNK_SIZE = 32768
-
-    with open(destination, 'wb') as f:
-        for chunk in response.iter_content(CHUNK_SIZE):
-            if chunk:  # filter out keep-alive new chunks
-                f.write(chunk)
-
-
-def download_data(graph_group, graph_name, graph_key):
-    dst = add_graph_dir(graph_group)
-
-    arch_dst = Path(os.path.join(dst, f'{graph_name}.tar.xz'))
-
-    download_file_from_google_drive(graph_key, arch_dst)
-
-    return dst
-
-
-def to_file(filepath, graph):
-    with open(filepath, 'w') as out_file:
-        for t in graph:
-            s, p, o = t[0], t[1], t[2]
-            out_file.write(f'{s} {p} {o}\n')
-
-
-def unpack_archive_listdir(dir, arch):
-    tmp = f'{dir}/tmp'
-    os.mkdir(tmp)
-    shutil.unpack_archive(arch, tmp)
-    result = os.listdir(tmp)
-    shutil.rmtree(tmp)
-    return result
-
-
-def unpack_graph(graph_group, graph_name):
-    to = Path(os.path.join(DATA_ROOT_DIR, graph_group, GRAPHS_DIR))
-
-    arch = Path(os.path.join(to, f'{graph_name}.tar.xz'))
-
-    shutil.unpack_archive(arch, to)
-
-    graph = unpack_archive_listdir(to, arch)[0]
-
-    os.remove(arch)
-
-    return os.path.join(to, graph)
-
-
-def clean_dir(name):
-    path = Path(os.path.join(DATA_ROOT_DIR, name, GRAPHS_DIR))
-    if os.path.isdir(path):
-        shutil.rmtree(path)
-    os.mkdir(path)
-
-
-def add_graph_dir(name):
-    dst = Path(os.path.join(DATA_ROOT_DIR, name, GRAPHS_DIR))
-    dst.mkdir(parents=True, exist_ok=True)
-    return f'{dst}'
-
-
-def gen_sierpinski_graph(target_dir, degree, predicates=['A']):
-    """ Generates a Sierpinski Triangle graph. """
-
-    def sierpinski(t, l, r, deg, preds, g):
-        """ Core function for generating the Sierpinski Triangle. """
-
-        if deg > 0:
-            lt = next(ids)
-            tr = next(ids)
-            rl = next(ids)
-            sierpinski(l, lt, rl, deg - 1, preds, g)
-            sierpinski(lt, t, tr, deg - 1, preds, g)
-            sierpinski(rl, tr, r, deg - 1, preds, g)
-        else:
-            add_edges(l, t, preds, g)
-            add_edges(t, r, preds, g)
-            add_edges(r, l, preds, g)
-
-    def add_edges(u, v, preds, g):
-        """ Adds edges between vertices u and v for all predicates. """
-
-        for p in preds:
-            g += [[u, p, v]]
-            g += [[v, p, u]]
-
-    def _idgen():
-        """ Generates integer identifiers for vertices. """
-
-        c = 4
-        while True:
-            yield c
-            c += 1
-
-    ids = _idgen()
-    graph = []
-    sierpinski(1, 2, 3, degree, predicates, graph)
-
-    with open(os.path.join(target_dir, f'sierpinskigraph_{degree}.txt'), 'w') as out_file:
-        for triple in graph:
-            out_file.write(f'{triple[0]} {triple[1]} {triple[2]} \n')
diff --git a/cfpq_data/src/utils/__init__.py b/cfpq_data/src/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/cfpq_data/src/utils/cmd_parser_interface.py b/cfpq_data/src/utils/cmd_parser_interface.py
new file mode 100644
index 00000000..3e7bc26b
--- /dev/null
+++ b/cfpq_data/src/utils/cmd_parser_interface.py
@@ -0,0 +1,35 @@
+from abc import ABC, abstractmethod
+from argparse import ArgumentParser, Namespace
+
+
+class ICmdParser(ABC):
+    """
+    Generic command line parser interface
+
+    """
+
+    @staticmethod
+    @abstractmethod
+    def init_cmd_parser(parser: ArgumentParser):
+        """
+        Initialize command line parser
+
+        :param parser: WorstCase subparser of command line parser
+        :type parser: ArgumentParser
+        :return: None
+        :rtype: None
+        """
+
+        parser.parse_args()
+
+    @staticmethod
+    @abstractmethod
+    def eval_cmd_parser(args: Namespace):
+        """
+        Evaluate command line parser
+
+        :param args: command line arguments
+        :type args: Namespace
+        :return: None
+        :rtype: None
+        """
diff --git a/cfpq_data/src/utils/rdf_graphs_downloader.py b/cfpq_data/src/utils/rdf_graphs_downloader.py
new file mode 100644
index 00000000..666cc99a
--- /dev/null
+++ b/cfpq_data/src/utils/rdf_graphs_downloader.py
@@ -0,0 +1,47 @@
+import requests
+from tqdm import tqdm
+
+from cfpq_data.src.utils.utils import add_graph_dir
+
+
+# TODO: rewrite to work with Google API
+def download_file_from_google_drive(file_id, destination):
+    download_file_prefix = 'https://docs.google.com/uc?export=download'
+
+    session = requests.Session()
+
+    response = session.get(download_file_prefix, params={'id': file_id}, stream=True)
+    token = get_confirm_token(response)
+
+    if token:
+        params = {'id': file_id, 'confirm': token}
+        response = session.get(download_file_prefix, params=params, stream=True)
+
+    save_response_content(response, destination)
+
+
+def get_confirm_token(response):
+    for key, value in response.cookies.items():
+        if key.startswith('download_warning'):
+            return value
+
+    return None
+
+
+def save_response_content(response, destination):
+    chunk_size = 32768
+
+    with open(destination, 'wb') as f:
+        for chunk in tqdm(response.iter_content(chunk_size), desc='Downloading'):
+            if chunk:  # filter out keep-alive new chunks
+                f.write(chunk)
+
+
+def download_data(graph_group, graph_name, graph_key):
+    dst = add_graph_dir(graph_group)
+
+    arch_dst = dst / f'{graph_name}.tar.xz'
+
+    download_file_from_google_drive(graph_key, arch_dst)
+
+    return dst
diff --git a/cfpq_data/src/utils/rdf_helper.py b/cfpq_data/src/utils/rdf_helper.py
new file mode 100644
index 00000000..dbdd3f29
--- /dev/null
+++ b/cfpq_data/src/utils/rdf_helper.py
@@ -0,0 +1,60 @@
+from pathlib import Path
+from typing import Dict
+
+from rdflib import Graph, BNode, URIRef
+
+from cfpq_data.config import RELEASE_INFO
+
+
+def write_to_rdf(target_path: Path, graph: Graph):
+    """
+    Saves the graph along the specified path
+
+    :param target_path: path where to save the graph
+    :type target_path: Path
+    :param graph: graph to be saved
+    :type graph: rdflib.Graph
+    :return: None
+    :rtype: None
+    """
+
+    graph.serialize(destination=str(target_path), format='xml')
+
+
+def add_rdf_edge(subj: int, pred: str, obj: int,
+                 rdf_graph: Graph,
+                 config: Dict[str, str] = RELEASE_INFO['Generators_Config'],
+                 reverse: bool = False) -> None:
+    """
+    Add edge to RDF graph
+
+    :param subj: number of the starting vertex
+    :type subj: int
+    :param pred: the label of the edge to be added
+    :type pred: str
+    :param obj: number of the ending vertex
+    :type obj: int
+    :param rdf_graph: the graph to which the edge will be added
+    :type rdf_graph: rdflib.Graph
+    :param config: edge configuration
+    :type config: Dict[str, str]
+    :param reverse: a label indicating whether to add a reverse edge to the graph
+    :type reverse: bool
+    :return: None
+    :rtype: None
+    """
+
+    s = BNode(f'id-{subj}')
+
+    p_text = config[pred]
+    if not p_text.startswith('http'):
+        p_text = f'http://yacc/rdf-schema#{p_text}'
+    p = URIRef(p_text)
+
+    o = BNode(f'id-{obj}')
+
+    rdf_graph.add((s, p, o))
+
+    if reverse is True:
+        pr = URIRef(p_text + 'R')
+        rdf_graph.add((s, pr, o))
diff --git a/cfpq_data/src/utils/utils.py b/cfpq_data/src/utils/utils.py
new file mode 100644
index 00000000..d0656eb7
--- /dev/null
+++ b/cfpq_data/src/utils/utils.py
@@ -0,0 +1,82 @@
+import os
+import shutil
+from pathlib import Path
+from typing import List
+
+from cfpq_data.config import MAIN_FOLDER
+
+
+def __unpack_archive_listdir(target_dir: Path, arch: str) -> List[str]:
+    """
+    Returns a list of files from the archive
+
+    :param target_dir: folder where the archive will be unpacked
+    :type target_dir: Path
+    :param arch: path to archive
+    :type arch: str
+    :return: list of files from the archive
+    :rtype: List[str]
+    """
+
+    tmp = target_dir / 'tmp'
+    os.mkdir(tmp)
+    shutil.unpack_archive(arch, tmp)
+    result = os.listdir(tmp)
+    shutil.rmtree(tmp)
+    return result
+
+
+def unpack_graph(graph_group: str, graph_name: str) -> str:
+    """
+    Unpacks the graph to the desired folder
+
+    :param graph_group: graph group type
+    :type graph_group: str
+    :param graph_name: graph name
+    :type graph_name: str
+    :return: path to the unpacked graph
+    :rtype: str
+    """
+
+    to = MAIN_FOLDER / 'data' / graph_group / 'Graphs'
+
+    arch = to / f'{graph_name}.tar.xz'
+
+    shutil.unpack_archive(arch, to)
+
+    graph = __unpack_archive_listdir(to, arch)[0]
+
+    os.remove(arch)
+
+    return os.path.join(to, graph)
+
+
+def clean_dir(name: str) -> None:
+    """
+    Clears the specified folder
+
+    :param name: folder to clear
+    :type name: str
+    :return: None
+    :rtype: None
+    """
+
+    path = MAIN_FOLDER / 'data' / name / 'Graphs'
+    if os.path.isdir(path):
+        shutil.rmtree(path)
+    os.mkdir(path)
+
+
+def add_graph_dir(name: str) -> Path:
+    """
+    Creates a folder for the specified graph type
+
+    :param name: specified graph type
+    :type name: str
+    :return: path to folder for the specified graph type
+    :rtype: Path
+    """
+
+    dst = MAIN_FOLDER / 'data' / name / 'Graphs'
+    dst.mkdir(parents=True, exist_ok=True)
+    return dst
diff --git a/docs/README.md b/docs/README.md
index 25f1ca88..b71c8182 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -133,9 +133,8 @@ Grammars list contains the following variants of same-generation query over diff
 ### Graphs information
 
 #### RDF
-
 | Name | Vertices | Edges | Size of file (Bytes) |
-|:---:|:---:|:---:|:---:|
+|:---|:---|:---|:---|
 | atom-primitive.owl | 291 | 425 | 48884 |
 | biomedical-mesure-primitive.owl | 341 | 459 | 53057 |
 | core.owl | 1323 | 2752 | 270846 |
@@ -156,9 +155,8 @@ Grammars list contains the following variants of same-generation query over diff
 | wine.rdf | 733 | 1839 | 78225 |
 
 #### MemoryAliases
-
 | Name | Vertices | Edges | Size of file (Bytes) |
-|:---:|:---:|:---:|:---:|
+|:---|:---|:---|:---|
 | Apache_httpd_2.2.18_pointsto_graph.xml | 1721418 | 1510411 | 138037427 |
 | arch_afterInline.txt.xml | 3448422 | 2970242 | 278669103 |
 | block_afterInline.txt.xml | 3423234 | 2951393 | 276869199 |
diff --git a/meta_table_for_README.py b/meta_table_for_readme.py
similarity index 89%
rename from meta_table_for_README.py
rename to meta_table_for_readme.py
index 0d853086..465c9f7d 100644
--- a/meta_table_for_README.py
+++ b/meta_table_for_readme.py
@@ -1,11 +1,12 @@
-import json
 import glob
-from cfpq_data.src.utils import get_info
+import json
+
+from cfpq_data.config import RELEASE_INFO
 
 
 def rdf_dict():
     data_rdf = dict()
-    names_rdf = get_info()['RDF']
+    names_rdf = RELEASE_INFO['RDF']
     for name in names_rdf:
         if name == "taxonomy-hierarchy" or name == "taxonomy":
             continue
@@ -16,7 +17,7 @@ def rdf_dict():
 
 def memoryaliases_dict():
     data_memoryaliases = dict()
-    names_memoryaliases = get_info()['MemoryAliases']
+    names_memoryaliases = RELEASE_INFO['MemoryAliases']
     for name in names_memoryaliases:
         name_of_file = glob.glob('./cfpq_data/data/MemoryAliases/Graphs/' + name + '*')[0]
         with open(name_of_file, 'r') as graph_info:
@@ -28,16 +29,16 @@ def create_table():
     column_names = ["name", "vertices", "edges", "size of file"]
     table_rdf = \
         "| Name | Vertices | Edges | Size of file (Bytes) |\n" + \
-        "|:---:|:---:|:---:|:---:|\n"
+        "|:---|:---|:---|:---|\n"
 
     table_memoryaliases = \
         "| Name | Vertices | Edges | Size of file (Bytes) |\n" + \
-        "|:---:|:---:|:---:|:---:|\n"
+        "|:---|:---|:---|:---|\n"
 
     rdf = rdf_dict()
     memory_aliases = memoryaliases_dict()
-    names_rdf = get_info()['RDF']
-    names_memoryaliases = get_info()['MemoryAliases']
+    names_rdf = RELEASE_INFO['RDF']
+    names_memoryaliases = RELEASE_INFO['MemoryAliases']
 
     for name in names_rdf:
         if name == "taxonomy-hierarchy" or name == "taxonomy":
diff --git a/reference_values.csv b/reference_values.csv
deleted file mode 100644
index 38b7378c..00000000
--- a/reference_values.csv
+++ /dev/null
@@ -1,87 +0,0 @@
-graph,grammar,control_sum
-fullgraph_10,A_star0,"{'s': 100}"
-fullgraph_100,A_star0,"{'s': 10000}"
-fullgraph_1000,A_star0,"{'s': 1000000}"
-fullgraph_200,A_star0,"{'s': 40000}"
-fullgraph_2000,A_star0,"{'s': 4000000}"
-fullgraph_50,A_star0,"{'s': 2500}"
-fullgraph_500,A_star0,"{'s': 250000}"
-fullgraph_5000,A_star0,"{'s': 25000000}"
-fullgraph_10,A_star1,"{'s': 100}"
-fullgraph_100,A_star1,"{'s': 10000}"
-fullgraph_1000,A_star1,"{'s': 1000000}"
-fullgraph_200,A_star1,"{'s': 40000}"
-fullgraph_2000,A_star1,"{'s': 4000000}"
-fullgraph_50,A_star1,"{'s': 2500}"
-fullgraph_500,A_star1,"{'s': 250000}"
-fullgraph_5000,A_star1,"{'s': 25000000}"
-fullgraph_10,A_star2,"{'s': 100}"
-fullgraph_100,A_star2,"{'s': 10000}"
-fullgraph_1000,A_star2,"{'s': 1000000}"
-fullgraph_200,A_star2,"{'s': 40000}"
-fullgraph_2000,A_star2,"{'s': 4000000}"
-fullgraph_50,A_star2,"{'s': 2500}"
-fullgraph_500,A_star2,"{'s': 250000}"
-fullgraph_5000,A_star2,"{'s': 25000000}"
-free_scale_graph_100_1,an_bm_cm_dn,"{'s': 0}"
-free_scale_graph_100_10,an_bm_cm_dn,"{'s': 1187}"
-free_scale_graph_100_3,an_bm_cm_dn,"{'s': 43}"
-free_scale_graph_100_5,an_bm_cm_dn,"{'s': 314}"
-free_scale_graph_10000_1,an_bm_cm_dn,"{'s': 28}"
-free_scale_graph_10000_10,an_bm_cm_dn,"{'s': 649360}"
-free_scale_graph_10000_3,an_bm_cm_dn,"{'s': 5441}"
-free_scale_graph_10000_5,an_bm_cm_dn,"{'s': 65560}"
-free_scale_graph_2500_1,an_bm_cm_dn,"{'s': 3}"
-free_scale_graph_2500_10,an_bm_cm_dn,"{'s': 86446}"
-free_scale_graph_2500_3,an_bm_cm_dn,"{'s': 1098}"
-free_scale_graph_2500_5,an_bm_cm_dn,"{'s': 12416}"
-free_scale_graph_500_1,an_bm_cm_dn,"{'s': 0}"
-free_scale_graph_500_10,an_bm_cm_dn,"{'s': 8122}"
-free_scale_graph_500_3,an_bm_cm_dn,"{'s': 551}"
-free_scale_graph_500_5,an_bm_cm_dn,"{'s': 1975}"
-worstcase_128,Brackets,"{'s': 4160}"
-worstcase_16,Brackets,"{'s': 72}"
-worstcase_256,Brackets,"{'s': 16512}"
-worstcase_32,Brackets,"{'s': 272}"
-worstcase_4,Brackets,"{'s': 6}"
-worstcase_512,Brackets,"{'s': 65792}"
-worstcase_64,Brackets,"{'s': 1056}"
-worstcase_8,Brackets,"{'s': 20}"
-geospeices,geo,"{'s': 226669749}"
-atom-primitive,GPPerf1,"{'s': 15447}"
-biomedical-mesure-primitive,GPPerf1,"{'s': 15156}"
-core,GPPerf1,"{'s': 316}"
-eclass_514en,GPPerf1,"{'s': 90994}"
-enzyme,GPPerf1,"{'s': 396}"
-foaf,GPPerf1,"{'s': 4118}"
-funding,GPPerf1,"{'s': 17610}"
-generations,GPPerf1,"{'s': 2164}"
-go-hierarchy,GPPerf1,"{'s': 588976}"
-go,GPPerf1,"{'s': 304070}"
-pathways,GPPerf1,"{'s': 884}"
-people_pets,GPPerf1,"{'s': 9472}"
-pizza,GPPerf1,"{'s': 56195}"
-skos,GPPerf1,"{'s': 799}"
-travel,GPPerf1,"{'s': 2499}"
-univ-bench,GPPerf1,"{'s': 2540}"
-wine,GPPerf1,"{'s': 66572}"
-atom-primitive,GPPerf2,"{'s': 122}"
-biomedical-mesure-primitive,GPPerf2,"{'s': 2856}"
-core,GPPerf2,"{'s': 214}"
-eclass_514en,GPPerf2,"{'s': 96163}"
-enzyme,GPPerf2,"{'s': 8163}"
-foaf,GPPerf2,"{'s': 10}"
-funding,GPPerf2,"{'s': 1158}"
-generations,GPPerf2,"{'s': 0}"
-go-hierarchy,GPPerf2,"{'s': 738937}"
-go,GPPerf2,"{'s': 334850}"
-pathways,GPPerf2,"{'s': 3117}"
-people_pets,GPPerf2,"{'s': 37}"
-pizza,GPPerf2,"{'s': 1262}"
-skos,GPPerf2,"{'s': 1}"
-travel,GPPerf2,"{'s': 63}"
-univ-bench,GPPerf2,"{'s': 81}"
-wine,GPPerf2,"{'s': 133}"
-G10k-0.001,SG,"{'s': 98545363}"
-G10k-0.01,SG,"{'s': 100000000}"
-G5k-0.001,SG,"{'s': 19960645}"
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 37baab70..06a058a1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,3 +8,6 @@ redis==3.3.10
 git+https://github.com/RedisGraph/redisgraph-py.git@275cf4c7609a061e55b538e10efbc02c91c090e7
 requests
 pytest
+setuptools
+redisgraph
+wget
diff --git a/setup.py b/setup.py
index a648c188..3d701b64 100644
--- a/setup.py
+++ b/setup.py
@@ -1,14 +1,28 @@
-from setuptools import setup, find_packages
+import sys
+
+from setuptools import *
+
+from cfpq_data.config import *
+
+sys.path.append(MAIN_FOLDER)
 
 setup(
-    name='cfpq_data'
-    , version='0.0.0'
-    , packages=find_packages()
+    name=PACKAGE_NAME
+    , version=PACKAGE_VERSION
+    , description='Graphs and grammars for experimental analysis of context-free path querying algorithms'
+    , long_description=Path(__file__).parent / 'README.md'
+    , long_description_content_type='text/markdown'
+    , packages=find_packages(exclude='tools')  # better to completely remove tools in the next versions
+    , package_dir=MAIN_FOLDER
     , url='https://github.com/JetBrains-Research/CFPQ_Data'
     , license='Apache License 2.0'
     , author='Grigorev Semyon'
     , author_email='rsdpisuy@gmail.com'
-    , description='Graphs and grammars for experimental analysis of context-free path querying algorithms'
     , package_data={'': ['*.json'], 'cfpq_data': ['data/*/Grammars/*.txt']}
     , include_package_data=True
+    , entry_points={
+        'console_scripts': [
+            'cfpq_data = cfpq_data.cmd_parser:cmd_parser'
+        ]
+    }
 )


Test patch:
diff --git a/tests/FullGraph/conftest.py b/tests/FullGraph/conftest.py
new file mode 100644
index 00000000..0de4e668
--- /dev/null
+++ b/tests/FullGraph/conftest.py
@@ -0,0 +1,6 @@
+import pytest
+
+
+@pytest.fixture(scope='session', params=[vertices for vertices in range(100, 1000, 100)])
+def num_of_vertices(request):
+    return request.param
diff --git a/tests/FullGraph/test_fullgraph.py b/tests/FullGraph/test_fullgraph.py
new file mode 100644
index 00000000..a115f760
--- /dev/null
+++ b/tests/FullGraph/test_fullgraph.py
@@ -0,0 +1,19 @@
+import os
+
+from cfpq_data import FullGraph
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_fullgraph(num_of_vertices):
+    first_graph = FullGraph.build(num_of_vertices)
+    second_graph_path = first_graph.save('tmp')
+    second_graph = FullGraph.load_from_rdf(second_graph_path)
+    os.remove(second_graph_path)
+
+    assert check_metadata(first_graph, second_graph)
diff --git a/tests/LUBM/conftest.py b/tests/LUBM/conftest.py
new file mode 100644
index 00000000..4173fae9
--- /dev/null
+++ b/tests/LUBM/conftest.py
@@ -0,0 +1,6 @@
+import pytest
+
+
+@pytest.fixture(scope='session', params=[1, 2, 3])
+def num_of_vertices(request):
+    return request.param
diff --git a/tests/LUBM/test_lubm.py b/tests/LUBM/test_lubm.py
new file mode 100644
index 00000000..3660f311
--- /dev/null
+++ b/tests/LUBM/test_lubm.py
@@ -0,0 +1,19 @@
+import os
+
+from cfpq_data import LUBM
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_lubm(num_of_vertices):
+    first_graph = LUBM.build(num_of_vertices)
+    second_graph_path = first_graph.save('tmp')
+    second_graph = LUBM.load_from_rdf(second_graph_path)
+    os.remove(second_graph_path)
+
+    assert check_metadata(first_graph, second_graph)
diff --git a/tests/MemoryAliases/conftest.py b/tests/MemoryAliases/conftest.py
index 1869c0d0..38a43f96 100644
--- a/tests/MemoryAliases/conftest.py
+++ b/tests/MemoryAliases/conftest.py
@@ -1,13 +1,16 @@
 import pytest
 
-from cfpq_data.src.utils import get_info
+from cfpq_data.config import RELEASE_INFO
 
-mas = get_info()['MemoryAliases']
+mas = RELEASE_INFO['MemoryAliases']
 
 
 @pytest.fixture(scope='session', params=[
-    name
-    for name, _ in mas.items()
+    'bzip2'
+    , 'gzip'
+    , 'ls'
+    , 'pr'
+    , 'wc'
 ])
 def ma_graph_name(request):
     return request.param
diff --git a/tests/MemoryAliases/test_MemoryAliases_graph.py b/tests/MemoryAliases/test_MemoryAliases_graph.py
deleted file mode 100644
index 834d95b1..00000000
--- a/tests/MemoryAliases/test_MemoryAliases_graph.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from cfpq_data import MemoryAliases
-
-
-def test_graph(ma_graph_name):
-    ma_graph = MemoryAliases(ma_graph_name)
-
-    actual_metadata = ma_graph.get_metadata()
-
-    actual_number_of_vertices = actual_metadata['vertices']
-    actual_number_of_edges = actual_metadata['edges']
-
-    expected_number_of_vertices = len(ma_graph.graph.all_nodes())
-    expected_number_of_edges = len(ma_graph.graph)
-
-    assert actual_number_of_vertices == expected_number_of_vertices
-    assert actual_number_of_edges == expected_number_of_edges
diff --git a/tests/MemoryAliases/test_memoryaliases_graph.py b/tests/MemoryAliases/test_memoryaliases_graph.py
new file mode 100644
index 00000000..933b32b2
--- /dev/null
+++ b/tests/MemoryAliases/test_memoryaliases_graph.py
@@ -0,0 +1,22 @@
+import os
+
+from cfpq_data import MemoryAliases
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_graph(ma_graph_name):
+    first_graph = MemoryAliases.build(ma_graph_name).save('tmp1')
+    first_graph_obj = MemoryAliases.build(first_graph)
+    second_graph = MemoryAliases.build(ma_graph_name).save('tmp2')
+    second_graph_obj = MemoryAliases.build(second_graph)
+
+    os.remove(first_graph)
+    os.remove(second_graph)
+
+    assert check_metadata(first_graph_obj, second_graph_obj)
diff --git a/tests/RDF/conftest.py b/tests/RDF/conftest.py
index c8b2c5c6..f2b7385a 100644
--- a/tests/RDF/conftest.py
+++ b/tests/RDF/conftest.py
@@ -1,14 +1,22 @@
 import pytest
 
-from cfpq_data.src.utils import get_info
+from cfpq_data.config import RELEASE_INFO
 
-rdfs = get_info()['RDF']
+rdfs = RELEASE_INFO['RDF']
 
 
 @pytest.fixture(scope='session', params=[
-    name
-    for name, _ in rdfs.items()
-    if name not in {'taxonomy-hierarchy', 'taxonomy'}
+    'atom-primitive'
+    , 'biomedical-mesure-primitive'
+    , 'core'
+    , 'foaf'
+    , 'funding'
+    , 'generations'
+    , 'people_pets'
+    , 'skos'
+    , 'travel'
+    , 'univ-bench'
+    , 'wine'
 ])
 def rdf_graph_name(request):
     return request.param
diff --git a/tests/RDF/test_RDF_graph.py b/tests/RDF/test_RDF_graph.py
deleted file mode 100644
index 80ea48a5..00000000
--- a/tests/RDF/test_RDF_graph.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from cfpq_data import RDF
-
-
-def test_graph(rdf_graph_name):
-    rdf_graph = RDF(rdf_graph_name)
-
-    actual_metadata = rdf_graph.get_metadata()
-
-    actual_number_of_vertices = actual_metadata['vertices']
-    actual_number_of_edges = actual_metadata['edges']
-
-    expected_number_of_vertices = len(rdf_graph.graph.all_nodes())
-    expected_number_of_edges = len(rdf_graph.graph)
-
-    assert actual_number_of_vertices == expected_number_of_vertices
-    assert actual_number_of_edges == expected_number_of_edges
diff --git a/tests/RDF/test_rdf.py b/tests/RDF/test_rdf.py
new file mode 100644
index 00000000..c3182eee
--- /dev/null
+++ b/tests/RDF/test_rdf.py
@@ -0,0 +1,22 @@
+import os
+
+from cfpq_data import RDF
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_rdf(rdf_graph_name):
+    first_graph = RDF.build(rdf_graph_name).save('tmp1')
+    first_graph_obj = RDF.build(first_graph)
+    second_graph = RDF.build(rdf_graph_name).save('tmp2')
+    second_graph_obj = RDF.build(second_graph)
+
+    os.remove(first_graph)
+    os.remove(second_graph)
+
+    assert check_metadata(first_graph_obj, second_graph_obj)
diff --git a/tests/ScaleFree/conftest.py b/tests/ScaleFree/conftest.py
new file mode 100644
index 00000000..ae76454c
--- /dev/null
+++ b/tests/ScaleFree/conftest.py
@@ -0,0 +1,10 @@
+import pytest
+
+
+@pytest.fixture(scope='session', params=[
+    (vertices_number, vertices_degree)
+    for vertices_number in range(100, 1000, 100)
+    for vertices_degree in range(3, 10)
+])
+def suit_scalefree(request):
+    return request.param
diff --git a/tests/ScaleFree/test_scalefree.py b/tests/ScaleFree/test_scalefree.py
new file mode 100644
index 00000000..29dc39ba
--- /dev/null
+++ b/tests/ScaleFree/test_scalefree.py
@@ -0,0 +1,20 @@
+import os
+
+from cfpq_data import ScaleFree
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_scalefree(suit_scalefree):
+    vertices_number, vertices_degree = suit_scalefree
+    first_graph = ScaleFree.build(vertices_number, vertices_degree)
+    second_graph_path = first_graph.save('tmp')
+    second_graph = ScaleFree.load(second_graph_path)
+    os.remove(second_graph_path)
+
+    assert check_metadata(first_graph, second_graph)
diff --git a/tests/SparseGraph/conftest.py b/tests/SparseGraph/conftest.py
new file mode 100644
index 00000000..a975b349
--- /dev/null
+++ b/tests/SparseGraph/conftest.py
@@ -0,0 +1,12 @@
+import pytest
+
+list_of_probabilities = (0.1, 0.2, 0.3, 0.4)
+
+
+@pytest.fixture(scope='session', params=[
+    (vertices_number, edge_probability)
+    for vertices_number in range(100, 1000, 100)
+    for edge_probability in list_of_probabilities
+])
+def suit_sparsegraph(request):
+    return request.param
diff --git a/tests/SparseGraph/test_sparsegraph.py b/tests/SparseGraph/test_sparsegraph.py
new file mode 100644
index 00000000..81e0457e
--- /dev/null
+++ b/tests/SparseGraph/test_sparsegraph.py
@@ -0,0 +1,20 @@
+import os
+
+from cfpq_data import SparseGraph
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_sparse(suit_sparsegraph):
+    vertices_number, edge_probability = suit_sparsegraph
+    first_graph = SparseGraph.build(vertices_number, edge_probability)
+    second_graph_path = first_graph.save('tmp')
+    second_graph = SparseGraph.load(second_graph_path)
+    os.remove(second_graph_path)
+
+    assert check_metadata(first_graph, second_graph)
diff --git a/tests/WorstCase/conftest.py b/tests/WorstCase/conftest.py
new file mode 100644
index 00000000..0de4e668
--- /dev/null
+++ b/tests/WorstCase/conftest.py
@@ -0,0 +1,6 @@
+import pytest
+
+
+@pytest.fixture(scope='session', params=[vertices for vertices in range(100, 1000, 100)])
+def num_of_vertices(request):
+    return request.param
diff --git a/tests/WorstCase/test_worstcase.py b/tests/WorstCase/test_worstcase.py
new file mode 100644
index 00000000..f4fb100e
--- /dev/null
+++ b/tests/WorstCase/test_worstcase.py
@@ -0,0 +1,19 @@
+import os
+
+from cfpq_data import WorstCase
+
+
+def check_metadata(first_graph, second_graph):
+    for field in ['vertices', 'edges']:
+        if first_graph.get_metadata()[field] != second_graph.get_metadata()[field]:
+            return False
+    return True
+
+
+def test_worstcase(num_of_vertices):
+    first_graph = WorstCase.build(num_of_vertices)
+    second_graph_path = first_graph.save('tmp')
+    second_graph = WorstCase.load_from_rdf(second_graph_path)
+    os.remove(second_graph_path)
+
+    assert check_metadata(first_graph, second_graph)