diff --git a/.gitignore b/.gitignore
index a298af1e5435b5e90f256adaecd94c5af1a51261..99a234f7db027ae66da39cc04a9f6f3abdfd2d8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,5 @@ tuner_results
 tuner_results/
 *.sh
 *.ipynb
-logistics/
\ No newline at end of file
+logistics/
+networkx_doc/
\ No newline at end of file
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b7000de4853b1c4a9f75cdc1ced9f351ab02284d
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,109 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest epub
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  epub       to make an epub"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+	@echo "  gitwash   to update the gitwash documentation"
+
+
+clean:
+	-rm -rf build/*
+
+dist: html
+	test -d build/latex || make latex
+	make -C build/latex all-pdf
+	-rm -rf build/dist
+	(cd build/html; cp -r . ../../build/dist)
+	(cd build/dist && tar czf ../dist.tar.gz .)
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
+	@echo
+	@echo "Build finished. The HTML pages are in build/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) build/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in build/dirhtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in build/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in build/qthelp, like this:"
+	@echo "# qcollectiongenerator build/qthelp/test.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile build/qthelp/test.qhc"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) build/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
+	@echo
+	@echo "The overview file is in build/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in build/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) build/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in build/doctest/output.txt."
+
+latexpdf: latex
+	@echo "Running LaTeX files through latexmk..."
+	$(MAKE) -C build/latex all-pdf
+	@echo "latexmk finished; the PDF files are in build/latex."
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e0af1e3f90895ee5cdbc2927ccedf8970053c32
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,27 @@
+# Building docs
+
+We use Sphinx for generating the API and reference documentation.
+## Instructions
+
+Install the following Python packages needed to build the documentation by entering:
+
+```bash
+pip install sphinx sphinx-autodoc-typehints sphinx-rtd-theme
+```
+
+To build the HTML documentation, enter::
+
+```bash
+make html
+```
+
+in the ``doc/`` directory. This will generate a ``build/html`` subdirectory
+containing the built documentation.
+
+To build the PDF documentation, enter::
+
+```bash
+make latexpdf
+```
+
+You will need to have LaTeX installed for this.
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..12f28f5c0af5b4ba58545c094c660337223233cf
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,154 @@
+from datetime import date
+import sphinx_rtd_theme
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+    "sphinx.ext.autosummary",
+    "sphinx.ext.autodoc",
+    "sphinx_autodoc_typehints",
+    "sphinx.ext.coverage",
+    "sphinx.ext.doctest",
+    "sphinx.ext.intersphinx",
+    "sphinx.ext.mathjax",
+    "sphinx.ext.todo",
+    "sphinx.ext.viewcode",
+    "numpydoc",
+]
+always_document_param_types = True
+
+# generate autosummary pages
+autosummary_generate = True
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# The suffix of source filenames.
+source_suffix = ".rst"
+
+# The encoding of source files.
+source_encoding = "utf-8"
+
+# The master toctree document.
+master_doc = "index"
+
+# General substitutions.
+project = "PredTuner"
+copyright = f"2020-{date.today().year}, University of Illinois"
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+# today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+# unused_docs = ['']
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+add_module_names = False
+
+# show_authors = True
+
+# The name of the Pygments (syntax highlighting) style to use.
+# pygments_style = 'friendly'
+pygments_style = "sphinx"
+
+# A list of prefixs that are ignored when creating the module index. (new in Sphinx 0.6)
+# modindex_common_prefix = ["networkx."]
+
+# doctest_global_setup = "import networkx as nx"
+
+# Options for HTML output
+# -----------------------
+
+
+html_theme = "sphinx_rtd_theme"
+html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+html_theme_options = {
+    "canonical_url": "https://networkx.org/documentation/stable/",
+    "navigation_depth": 3,
+    "logo_only": True,
+}
+
+# html_logo = "_static/networkx_logo.svg"
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+# html_style = ''
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ["_static"]
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = "%b %d, %Y"
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Content template for the index page.
+# html_index = 'index.html'
+
+# Custom sidebar templates, maps page names to templates.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# templates.
+# html_additional_pages = {'': ''}
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+html_copy_source = False
+
+# Options for LaTeX output
+# ------------------------
+
+# Use a latex engine that allows for unicode characters in docstrings
+latex_engine = "xelatex"
+# The paper size ('letter' or 'a4').
+latex_paper_size = "letter"
+
+# The font size ('10pt', '11pt' or '12pt').
+# latex_font_size = '10pt'
+
+latex_appendices = ["tutorial"]
+
+# Intersphinx mapping
+intersphinx_mapping = {
+    "python": ("https://docs.python.org/3/", None),
+    "numpy": ("https://numpy.org/doc/stable/", None),
+    "matplotlib": ("https://matplotlib.org", None),
+    "scipy": ("https://docs.scipy.org/doc/scipy/reference", None),
+    "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None),
+    "pytorch": ("https://pytorch.org/docs/stable", None),
+}
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+default_role = "obj"
+
+numpydoc_show_class_members = False
+
+
+def setup(app):
+    app.add_css_file("custom.css")
+    app.add_js_file("copybutton.js")
diff --git a/doc/getting_started.rst b/doc/getting_started.rst
new file mode 100644
index 0000000000000000000000000000000000000000..75381aa720fdee717d20db115ecbc74e73deff4c
--- /dev/null
+++ b/doc/getting_started.rst
@@ -0,0 +1,2 @@
+Getting Started
+===================
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6866a98b3d779063329219cd2c49f8b2c6feb41d
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,46 @@
+.. _contents:
+
+PredTuner
+=============================
+
+PredTuner is a Python library for predictive approximation autotuning.
+
+PredTuner performs autotuning on approximation choices for a program
+using an error-predictive proxy instead of executing the program,
+to greatly speedup autotuning while getting results of comparable quality.
+
+PredTuner is a contribution of [ApproxTuner]
+(https://ppopp21.sigplan.org/details/PPoPP-2021-main-conference/41/ApproxTuner-A-Compiler-and-Runtime-System-for-Adaptive-Approximations).
+
+Short-term Goals
+- Measure accuracy impact of approximations
+- Obtain a tuned, approximated CNN in <5 lines of code
+- Easy to manage multiple approximation configs
+- Easy to load and manage prior tuning results
+- Flexible retraining support
+Possible Long-term Goals
+- High-performance implementations of approximate layers
+- Allow users to register their own approximations
+- Support for other frameworks: TF, ONNX, JAX
+
+Documentation
+-------------
+
+.. only:: html
+
+    :Release: |version|
+    :Date: |today|
+
+.. toctree::
+   :maxdepth: 1
+
+   getting_started
+   reference/index
+
+Indices and tables
+------------------
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+* :ref:`glossary`
diff --git a/doc/reference/index.rst b/doc/reference/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2a3a0a68b70eb678e956900f9a7291e11bc8fbe6
--- /dev/null
+++ b/doc/reference/index.rst
@@ -0,0 +1,11 @@
+PyTorch Autotuning API
+======================
+
+.. autoclass:: predtuner.torchapp.TorchApp
+   :members:
+   :undoc-members:
+
+.. autoclass:: predtuner.modeledapp.ApproxModeledTuner
+   :members:
+   :inherited-members:
+   :undoc-members:
diff --git a/predtuner/torchapp.py b/predtuner/torchapp.py
index 0160c0cc14950abe0df068d84934c76224ca0939..3ad4f8768a961c07e87064fc1ebf164b0d606a1f 100644
--- a/predtuner/torchapp.py
+++ b/predtuner/torchapp.py
@@ -44,10 +44,43 @@ _default_device = f"cuda" if torch.cuda.is_available() else "cpu"
 
 
 class TorchApp(ModeledApp, abc.ABC):
-    """Approximable PyTorch Modules (tensor output assumed).
-    Automatically derives performance model and QoS models P1&P2.
-    
-    knobs: User defines a set of all knobs available; we'll dispatch them to each layer (op).
+    r"""Adaptor for approximable PyTorch Modules with tensor output.
+
+    A TorchApp stores the PyTorch Module, datasets for tuning and calibration,
+    set of available TorchApproxKnob each of which may be applied to some layer in the Module,
+    and the quality of service (QoS) metric of application (e.g., accuracy).
+    It provides empirical tuning and predictive tuning capability (see `TorchApp.tune()`),
+
+    Parameters
+    ----------
+    app_name:
+        Name of the application, which is used as an identifier in tuning sessions, etc.
+    module:
+        The PyTorch module to tune.
+    tune_dataloader:
+        A dataset to use as inputs to module during tuning. (PyTorch DataLoader is conceptually
+        an enumerable, batched dataset.)
+    test_dataloader:
+        A input dataset used for QoS testing (see `test_config` parameter of `ApproxModeledTuner.tune`).
+    knobs:
+        A set of knobs to be considered. Each knob has an `is_applicable()` method
+        which is used to determine which layer it can apply to.
+    tensor_to_qos:
+        QoS metric function which computes QoS from the module's output.
+    combine_qos:
+        A function to combine each batch's QoS into one value.
+        When QoS is accuracy this will most likely be `mean()` (which is the default).
+    device:
+        The device to store module and perform inference on. By default is "cuda"
+        if CUDA is available, otherwise "cpu".
+    model_storage_folder:
+        A folder to store the serialized QoS models into.
+        `QoSModelP1` will be serialized into `model_storage_folder / "p1.pkl"`,
+        and `QoSModelP2` into `model_storage_folder / "p2.json"`.
+        See `QoSModelP1` and `QoSModelP2` for details.
+
+    Attributes
+    ----------
     """
 
     def __init__(
@@ -60,7 +93,7 @@ class TorchApp(ModeledApp, abc.ABC):
         tensor_to_qos: Callable[[torch.Tensor, Any], float],
         combine_qos: Callable[[np.ndarray], float] = np.mean,
         device: Union[torch.device, str] = _default_device,
-        model_storage_folder: Optional[PathLike] = None
+        model_storage_folder: Optional[PathLike] = None,
     ) -> None:
         self.app_name = app_name
         self.module = module
@@ -70,7 +103,9 @@ class TorchApp(ModeledApp, abc.ABC):
         self.tensor_to_qos = tensor_to_qos
         self.combine_qos = combine_qos
         self.device = device
-        self.model_storage = Path(model_storage_folder) if model_storage_folder else None
+        self.model_storage = (
+            Path(model_storage_folder) if model_storage_folder else None
+        )
 
         self.module = self.module.to(device)
         self.midx = ModuleIndexer(module)
@@ -92,13 +127,21 @@ class TorchApp(ModeledApp, abc.ABC):
 
     @property
     def name(self) -> str:
+        """Returns the name of application."""
         return self.app_name
 
     @property
     def op_knobs(self) -> Dict[str, List[ApproxKnob]]:
+        """Returns a list of applicable knobs for each operator (layer) in module."""
         return self._op_knobs
 
     def get_models(self) -> List[Union[IPerfModel, IQoSModel]]:
+        """Returns a list of predictive tuning models.
+
+        TorchApp in particular derives 1 performance model (LinearPerfModel)
+        and 2 QoS models (QoSModelP1, QoSModelP2) automatically.
+        """
+
         def batched_valset_qos(tensor_output: torch.Tensor):
             dataset_len = len(self.tune_loader.dataset)
             assert len(tensor_output) == dataset_len
@@ -115,7 +158,9 @@ class TorchApp(ModeledApp, abc.ABC):
         p2_storage = self.model_storage / "p2.json" if self.model_storage else None
         return [
             LinearPerfModel(self._op_costs, self._knob_speedups),
-            QoSModelP1(self, self._get_raw_output_valset, batched_valset_qos, p1_storage),
+            QoSModelP1(
+                self, self._get_raw_output_valset, batched_valset_qos, p1_storage
+            ),
             QoSModelP2(self, p2_storage),
         ]
 
@@ -123,6 +168,9 @@ class TorchApp(ModeledApp, abc.ABC):
     def empirical_measure_qos_perf(
         self, with_approxes: KnobsT, is_test: bool
     ) -> Tuple[float, float]:
+        """Measure the QoS and performance of Module with given approximation
+        empirically (i.e., by running the Module on the dataset)."""
+
         from time import time_ns
 
         dataloader = self.test_loader if is_test else self.tune_loader