diff --git a/README.md b/README.md
index c2d20756de2ca586447f4cee23d62d51dd64c592..b9f7294e5e08de101da4adbc51a148f1a2c8e393 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,16 @@
 
 Trying to deconvolve single tag assignment for multiple molecules
 
+## Installation
+
+Install the package from the root directory.
+```bash
+    # For users
+    pip install . --user
+    # For developers
+    pip install -e . --user
+```
+
 ## Scripts
 
 For the majority of the scripts, argparse is used.
diff --git a/deconvolution/d2graph/__init__.py b/deconvolution/d2graph/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/deconvolution/d2graph/d2_algorithms.py b/deconvolution/d2graph/d2_algorithms.py
index 1391181e24ead30872b1af6e3665b86eff8f7bb8..f54df1345325f49fb1881f0ddcbe48d860df4156 100644
--- a/deconvolution/d2graph/d2_algorithms.py
+++ b/deconvolution/d2graph/d2_algorithms.py
@@ -1,6 +1,6 @@
 import networkx as nx
 
-from d2graph.d2_path import Unitig
+from deconvolution.d2graph.d2_path import Unitig
 
 
 """ Remove unnecessary transitions
diff --git a/deconvolution/d2graph/d2_graph.py b/deconvolution/d2graph/d2_graph.py
index fc2c27fa49985ff46ca7d2cc9ab5e46d268d0e92..f7d6a77444148c723cceb6820c0df99331972a3c 100644
--- a/deconvolution/d2graph/d2_graph.py
+++ b/deconvolution/d2graph/d2_graph.py
@@ -3,8 +3,8 @@ import itertools
 from bidict import bidict
 import sys
 
-from dgraph.FixedDGIndex import FixedDGIndex
-from dgraph.d_graph import Dgraph, compute_all_max_d_graphs, list_domination_filter
+from deconvolution.dgraph.FixedDGIndex import FixedDGIndex
+from deconvolution.dgraph.d_graph import Dgraph, compute_all_max_d_graphs
 
 
 class D2Graph(nx.Graph):
diff --git a/deconvolution/d2graph/d2_reduction.py b/deconvolution/d2graph/d2_reduction.py
index dd534aca826800ccf8342eb8f939a66e0d22bab8..3e0250600c8a53db3fa99b2f33a1e6614258f9b4 100755
--- a/deconvolution/d2graph/d2_reduction.py
+++ b/deconvolution/d2graph/d2_reduction.py
@@ -4,7 +4,8 @@ import networkx as nx
 import argparse
 import sys
 
-from d2graph import d2_algorithms as d2a, d2_graph as d2
+from deconvolution.d2graph import d2_graph as d2
+from deconvolution.d2graph import d2_algorithms as d2a
 
 
 def parse_arguments():
diff --git a/deconvolution/d2graph/path_algorithms.py b/deconvolution/d2graph/path_algorithms.py
index e22c93c84661e7c0642086d5f1a8f585f4019a9a..d10a49c73262e966f4984d966d349d626e57701f 100644
--- a/deconvolution/d2graph/path_algorithms.py
+++ b/deconvolution/d2graph/path_algorithms.py
@@ -1,4 +1,4 @@
-from d2graph.d2_path import Path
+from deconvolution.d2graph.d2_path import Path
 
 """ Greedy algorithm. Start with th most probable unitig (ie lowest normalized penalty first and largest unitig for
     equalities). Then extends on both side to the nearest interesting unitig.
diff --git a/deconvolution/d2graph/path_optimization.py b/deconvolution/d2graph/path_optimization.py
index 180f07b758dea2719e302a3e7eb9c6927e9dc154..3dbde86dc5b1954f3260ce0015244f5879b2d876 100644
--- a/deconvolution/d2graph/path_optimization.py
+++ b/deconvolution/d2graph/path_optimization.py
@@ -1,7 +1,8 @@
 import random
-from d2graph.d2_path import Path
 import networkx as nx
 
+from deconvolution.d2graph.d2_path import Path
+
 
 class Optimizer:
 
diff --git a/deconvolution/dgraph/FixedDGIndex.py b/deconvolution/dgraph/FixedDGIndex.py
index a9860cc844e74bb82f6988bf6728dd8b7d7e9698..8edca6277df8216d13437288d61d11d596edc730 100644
--- a/deconvolution/dgraph/FixedDGIndex.py
+++ b/deconvolution/dgraph/FixedDGIndex.py
@@ -1,6 +1,7 @@
-from dgraph.AbstractDGIndex import AbstractDGIndex
 from itertools import combinations
 
+from deconvolution.dgraph.AbstractDGIndex import AbstractDGIndex
+
 
 class FixedDGIndex(AbstractDGIndex):
 
diff --git a/deconvolution/dgraph/VariableDGIndex.py b/deconvolution/dgraph/VariableDGIndex.py
index 7809bb047c10ea83e2d9407b1360fb2e79787b37..013bb9964c5fe19e100075d4d618acde5b8e8dc3 100644
--- a/deconvolution/dgraph/VariableDGIndex.py
+++ b/deconvolution/dgraph/VariableDGIndex.py
@@ -1,6 +1,7 @@
-from dgraph.AbstractDGIndex import AbstractDGIndex
 from itertools import combinations
 
+from deconvolution.dgraph.AbstractDGIndex import AbstractDGIndex
+
 
 class VariableDGIndex(AbstractDGIndex):
 
diff --git a/deconvolution/dgraph/d_graph.py b/deconvolution/dgraph/d_graph.py
index 5d10b0cef87052b85f3be8595be1007d5cbed644..e07ded4cedcc03b4b2229a49434b57d1c69b2415 100644
--- a/deconvolution/dgraph/d_graph.py
+++ b/deconvolution/dgraph/d_graph.py
@@ -1,8 +1,8 @@
 import networkx as nx
 from functools import total_ordering
-import community # pip install python-louvain
+import community # pip install python-louvain TODO: Ajouter dans les requirements !!!
 
-from dgraph.FixedDGIndex import FixedDGIndex
+from deconvolution.dgraph.FixedDGIndex import FixedDGIndex
 
 
 @total_ordering
@@ -312,92 +312,92 @@ def compute_all_max_d_graphs(graph, debug=False, clique_mode=None):
     return d_graphs
 
 
-""" Add the new dg in the dgs list. If dg is dominated by another dg in the list, then it's
-    dropped. If any dg in the list is dominated by the dg to add, then, the new dg is added and
-    all the dominated dg are removed from the list.
-    @param dg A new dg to add/filter.
-    @param undominated_dgs_list A list of dg where any of them is dominated by another one.
-    @return The updated undominated list. 
-"""
-def add_new_dg_regarding_domination(dg, undominated_dgs_list):
-    to_remove = []
-    dominated = False
-
-    # Search for domination relations
-    for u_dg in undominated_dgs_list:
-        if not dominated and dg.is_dominated(u_dg):
-            dominated = True
-        if u_dg.is_dominated(dg):
-            to_remove.append(u_dg)
-
-    # Remove dominated values
-    size = len(undominated_dgs_list)
-    for dg2 in to_remove:
-        undominated_dgs_list.remove(dg2)
-
-    # Add the new dg
-    if not dominated:
-        undominated_dgs_list.append(dg)
-
-    return undominated_dgs_list
-
-
-
-def filter_dominated(d_graphs, overall=False, in_place=True):
-    if not overall:
-        return local_domination_filter(d_graphs, in_place)
-
-    all_d_graphs = []
-    for dgs in d_graphs.values():
-        all_d_graphs.extend(dgs)
-
-    all_d_graphs = list_domination_filter(all_d_graphs)
-
-    return d_graphs
-
-
-""" Filter the d-graphs by node. In a list of d-graph centered on a node n, if a d-graph is
-    completely included in another and have a highest distance score to the optimal, then it is
-    filtered out.
-    @param d_graphs All the d-graphs to filter, sorted by central node.
-    @param in_place If true, modify the content of d_graph with the filtered version. If False,
-    copy all the content in a new dictionary.
-    @return The filtered dictionary of d-graph per node.
-"""
-def local_domination_filter(d_graphs, in_place=True):
-    filtered = d_graphs if in_place else {}
-
-    # Filter node by node
-    for node, d_graph_list in d_graphs.items():
-        lst = str(sorted([str(x) for x in d_graph_list]))
-        filtered[node] = brutal_list_domination_filter(d_graph_list, node_name=str(node))
-
-    return filtered
-
-
-""" Filter the input d-graphs list. In the list of d-graph centered on a node n, if a d-graph is
-    completely included in another and have a highest distance score to the optimal, then it is
-    filtered out.
-    @param d_graphs All the d-graphs to filter.
-    @return The filtered dictionary of d-graph per node.
-"""
-def list_domination_filter(d_graphs):
-    filtered = []
-
-    # Filter d-graph by d-graph
-    for dg in d_graphs:
-        add_new_dg_regarding_domination(dg, filtered)
-
-    return set(filtered)
-
-def brutal_list_domination_filter(d_graphs, node_name=""):
-    undominated = set(d_graphs)
-    to_remove = set()
-
-    for dg1 in d_graphs:
-        for dg2 in d_graphs:
-            if dg1.is_dominated(dg2):
-                to_remove.add(dg1)
-                break
-
-    return undominated - to_remove
+# """ Add the new dg in the dgs list. If dg is dominated by another dg in the list, then it's
+#     dropped. If any dg in the list is dominated by the dg to add, then, the new dg is added and
+#     all the dominated dg are removed from the list.
+#     @param dg A new dg to add/filter.
+#     @param undominated_dgs_list A list of dg where any of them is dominated by another one.
+#     @return The updated undominated list.
+# """
+# def add_new_dg_regarding_domination(dg, undominated_dgs_list):
+#     to_remove = []
+#     dominated = False
+#
+#     # Search for domination relations
+#     for u_dg in undominated_dgs_list:
+#         if not dominated and dg.is_dominated(u_dg):
+#             dominated = True
+#         if u_dg.is_dominated(dg):
+#             to_remove.append(u_dg)
+#
+#     # Remove dominated values
+#     size = len(undominated_dgs_list)
+#     for dg2 in to_remove:
+#         undominated_dgs_list.remove(dg2)
+#
+#     # Add the new dg
+#     if not dominated:
+#         undominated_dgs_list.append(dg)
+#
+#     return undominated_dgs_list
+#
+#
+#
+# def filter_dominated(d_graphs, overall=False, in_place=True):
+#     if not overall:
+#         return local_domination_filter(d_graphs, in_place)
+#
+#     all_d_graphs = []
+#     for dgs in d_graphs.values():
+#         all_d_graphs.extend(dgs)
+#
+#     all_d_graphs = list_domination_filter(all_d_graphs)
+#
+#     return d_graphs
+#
+#
+# """ Filter the d-graphs by node. In a list of d-graph centered on a node n, if a d-graph is
+#     completely included in another and have a highest distance score to the optimal, then it is
+#     filtered out.
+#     @param d_graphs All the d-graphs to filter, sorted by central node.
+#     @param in_place If true, modify the content of d_graph with the filtered version. If False,
+#     copy all the content in a new dictionary.
+#     @return The filtered dictionary of d-graph per node.
+# """
+# def local_domination_filter(d_graphs, in_place=True):
+#     filtered = d_graphs if in_place else {}
+#
+#     # Filter node by node
+#     for node, d_graph_list in d_graphs.items():
+#         lst = str(sorted([str(x) for x in d_graph_list]))
+#         filtered[node] = brutal_list_domination_filter(d_graph_list, node_name=str(node))
+#
+#     return filtered
+#
+#
+# """ Filter the input d-graphs list. In the list of d-graph centered on a node n, if a d-graph is
+#     completely included in another and have a highest distance score to the optimal, then it is
+#     filtered out.
+#     @param d_graphs All the d-graphs to filter.
+#     @return The filtered dictionary of d-graph per node.
+# """
+# def list_domination_filter(d_graphs):
+#     filtered = []
+#
+#     # Filter d-graph by d-graph
+#     for dg in d_graphs:
+#         add_new_dg_regarding_domination(dg, filtered)
+#
+#     return set(filtered)
+#
+# def brutal_list_domination_filter(d_graphs, node_name=""):
+#     undominated = set(d_graphs)
+#     to_remove = set()
+#
+#     for dg1 in d_graphs:
+#         for dg2 in d_graphs:
+#             if dg1.is_dominated(dg2):
+#                 to_remove.add(dg1)
+#                 break
+#
+#     return undominated - to_remove
diff --git a/deconvolution/main/__init__.py b/deconvolution/main/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/deconvolution/main/d2_to_path.py b/deconvolution/main/d2_to_path.py
index 444a14fffabb85c94240efff14c4b9ab3fdfe0ab..667f0c11ba6e5184f0c9f9927bd1e6014c1e0a14 100755
--- a/deconvolution/main/d2_to_path.py
+++ b/deconvolution/main/d2_to_path.py
@@ -4,7 +4,7 @@ import networkx as nx
 import argparse
 import sys
 
-from d2graph import d2_graph as d2, path_optimization as po
+from deconvolution.d2graph import d2_graph as d2, path_optimization as po
 
 
 def parse_arguments():
diff --git a/deconvolution/main/generate_fake_barcode_graph.py b/deconvolution/main/generate_fake_barcode_graph.py
index f61631d8e6fef593c5961dcd179c823de22b7183..9d218afe55c75f75dbd05522dc2887941364b0dd 100755
--- a/deconvolution/main/generate_fake_barcode_graph.py
+++ b/deconvolution/main/generate_fake_barcode_graph.py
@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 
-import networkx  as nx
+import networkx as nx
 import random
 import argparse
 
-from dgraph import graph_manipulator as gm
+import deconvolution.dgraph.graph_manipulator as gm
 
 
 def parse_arguments():
diff --git a/deconvolution/main/generate_fake_molecule_graph.py b/deconvolution/main/generate_fake_molecule_graph.py
index a859ff53c08f761eaae2c8afcc21620f95f673a9..be096630174dae464042e4b321d7d6d4dbf26dea 100755
--- a/deconvolution/main/generate_fake_molecule_graph.py
+++ b/deconvolution/main/generate_fake_molecule_graph.py
@@ -2,7 +2,7 @@
 
 import argparse
 
-from dgraph import graph_manipulator as gm
+from deconvolution.dgraph import graph_manipulator as gm
 
 
 def parse_arguments():
diff --git a/deconvolution/main/to_d2_graph.py b/deconvolution/main/to_d2_graph.py
index 4a2d1befe44d868aa7dbb66ec6dccca529534391..dc409acf7fd7ac63e6034e40fcaeb0948a2c8175 100755
--- a/deconvolution/main/to_d2_graph.py
+++ b/deconvolution/main/to_d2_graph.py
@@ -4,7 +4,7 @@ import networkx as nx
 import argparse
 import sys
 
-from d2graph import d2_graph as d2
+from deconvolution.d2graph import d2_graph as d2
 
 
 def parse_arguments():
diff --git a/setup.py b/setup.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..926e3d9018b7a7164f6c683de95408fb6865c555 100644
--- a/setup.py
+++ b/setup.py
@@ -0,0 +1,10 @@
+from distutils.core import setup
+
+
+setup(
+    name='10X-deconvolve',
+    version='0.1dev',
+    packages=['deconvolution.d2graph', 'deconvolution.dgraph', 'deconvolution.main'],
+    license='AGPL V3',
+    long_description=open('README.md').read(),
+)
diff --git a/tests/Index_test.py b/tests/Index_test.py
index 393d37a20b95d7ecbaa5408999bde8ae32b7a75d..9b880886d4cde530835c1d19556c9567e6df348f 100644
--- a/tests/Index_test.py
+++ b/tests/Index_test.py
@@ -1,10 +1,10 @@
 import unittest
-
 from random import randint
-from dgraph.FixedDGIndex import FixedDGIndex
-from dgraph.VariableDGIndex import VariableDGIndex
-from dgraph.d_graph import Dgraph
-from dgraph.graph_manipulator import generate_d_graph_chain
+
+from deconvolution.dgraph.FixedDGIndex import FixedDGIndex
+from deconvolution.dgraph.VariableDGIndex import VariableDGIndex
+from deconvolution.dgraph.d_graph import Dgraph
+from deconvolution.dgraph.graph_manipulator import generate_d_graph_chain
 
 
 class TestIndex(unittest.TestCase):
diff --git a/tests/d2_graph_test.py b/tests/d2_graph_test.py
index 7a72e5eae70a17b8c3191dff76dd77e2e5047e2d..5d787431fe32abd67ada66f80b9c615c79e9c422 100644
--- a/tests/d2_graph_test.py
+++ b/tests/d2_graph_test.py
@@ -3,10 +3,8 @@ import tempfile
 import networkx as nx
 from scipy.special import comb
 
-from d2graph.d2_graph import D2Graph
-from dgraph import graph_manipulator as gm
-
-from d_graph_data import complete_graph
+from deconvolution.d2graph.d2_graph import D2Graph
+from deconvolution.dgraph import graph_manipulator as gm
 
 
 class TestD2Graph(unittest.TestCase):
diff --git a/tests/d_graph_test.py b/tests/d_graph_test.py
index ddbc25903689b6d9f9c25a08041634379795e2d3..16b746c3c90d8c12f0e4ede5e9dc2e0bb775fc82 100644
--- a/tests/d_graph_test.py
+++ b/tests/d_graph_test.py
@@ -1,8 +1,8 @@
 import unittest
 
 from d_graph_data import unit_d_graph
-from dgraph.d_graph import Dgraph
-from dgraph import graph_manipulator as gm
+from deconvolution.dgraph.d_graph import Dgraph
+from deconvolution.dgraph import graph_manipulator as gm
 
 
 class TestDGraph(unittest.TestCase):
diff --git a/tests/graph_manipulation_test.py b/tests/graph_manipulation_test.py
index 4ff4a38480133804c3c18071d0b3eea34c2a0e92..da597dd9e78b880983ec75c351cbd15deca7b7bc 100644
--- a/tests/graph_manipulation_test.py
+++ b/tests/graph_manipulation_test.py
@@ -1,6 +1,6 @@
 import unittest
 
-from dgraph import graph_manipulator as gm
+from deconvolution.dgraph import graph_manipulator as gm
 
 
 class TestGraphManipulation(unittest.TestCase):