diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css
new file mode 100644
index 0000000..cb5e8ac
--- /dev/null
+++ b/docs/_static/css/custom.css
@@ -0,0 +1,245 @@
+a,
+a:hover,
+a:visited {
+    color: #ee4c2c;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+.wy-nav-content {
+    max-width: 960px;
+}
+
+.wy-menu-vertical header,
+.wy-menu-vertical p.caption {
+    color: #262626;
+}
+
+
+.wy-nav-side {
+    background: #f3f4f7;
+}
+
+.wy-menu-vertical a,
+.wy-menu-vertical a:active,
+.wy-menu-vertical a:hover {
+    color: #6c6c6d;
+}
+
+.wy-menu-vertical a:hover {
+    background-color: #e8e9ea;
+    cursor: pointer;
+}
+
+.wy-menu-vertical a:active {
+    background-color: #e8e9ea;
+}
+
+.wy-side-nav-search input[type=text] {
+    border-color: #c9c9c9;
+}
+
+.rst-content .viewcode-back,
+.rst-content .viewcode-link {
+    color: #4974D1;
+}
+
+html.writer-html4 .rst-content dl:not(.docutils)>dt,
+html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt {
+    background: #f3f4f7;
+    color: #6c6c6d;
+    border-top: none;
+    border-left: 3px solid #ee4c2c;
+    padding: 0.5rem;
+    padding-right: 100px;
+    word-wrap: break-word;
+}
+
+.rst-content table.docutils,
+.wy-table-bordered-all {
+    border: none;
+}
+
+.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,
+.wy-table-backed,
+.wy-table-odd td,
+.wy-table-striped tr:nth-child(2n-1) td {
+    background-color: #f3f4f7;
+}
+
+.rst-content table.docutils td,
+.wy-table-bordered-all td {
+    border: none;
+}
+
+.rst-content table.docutils td,
+.rst-content table.docutils th,
+.rst-content table.field-list td,
+.rst-content table.field-list th,
+.wy-table td,
+.wy-table th {
+    padding: 12px 16px;
+}
+
+.rst-content code,
+.rst-content tt,
+code {
+    border: none;
+    color: #ee4c2c;
+}
+
+.rst-content code.xref,
+.rst-content tt.xref,
+a .rst-content code,
+a .rst-content tt {
+    color: inherit;
+}
+
+.wy-menu-vertical li.toctree-l1.current>a {
+    border: none;
+}
+
+.wy-menu-vertical li.current a {
+    border: none;
+}
+
+.wy-menu-vertical li.current {
+    background: #e1e2e4;
+}
+
+.wy-menu-vertical li.current>a,
+.wy-menu-vertical li.on a,
+.wy-menu-vertical li.current>a:hover,
+.wy-menu-vertical li.on a:hover {
+    background: none;
+}
+
+
+.highlight {
+    background: #f3f4f7;
+}
+
+.rst-content div[class^=highlight],
+.rst-content pre.literal-block {
+    border: none;
+}
+
+html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt,
+html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dl:not(.field-list)>dt {
+    padding-left: 8px;
+    border: none;
+    border-left: 3px solid #ee4c2c;
+    background: #f3f4f7;
+    color: #555;
+}
+
+.rst-content .note,
+.rst-content .seealso,
+.rst-content .wy-alert-info.admonition,
+.rst-content .wy-alert-info.admonition-todo,
+.rst-content .wy-alert-info.attention,
+.rst-content .wy-alert-info.caution,
+.rst-content .wy-alert-info.danger,
+.rst-content .wy-alert-info.error,
+.rst-content .wy-alert-info.hint,
+.rst-content .wy-alert-info.important,
+.rst-content .wy-alert-info.tip,
+.rst-content .wy-alert-info.warning,
+.wy-alert.wy-alert-info {
+    background: #f3f4f7;
+}
+
+.rst-content .note .admonition-title,
+.rst-content .note .wy-alert-title,
+.rst-content .seealso .admonition-title,
+.rst-content .seealso .wy-alert-title,
+.rst-content .wy-alert-info.admonition-todo .admonition-title,
+.rst-content .wy-alert-info.admonition-todo .wy-alert-title,
+.rst-content .wy-alert-info.admonition .admonition-title,
+.rst-content .wy-alert-info.admonition .wy-alert-title,
+.rst-content .wy-alert-info.attention .admonition-title,
+.rst-content .wy-alert-info.attention .wy-alert-title,
+.rst-content .wy-alert-info.caution .admonition-title,
+.rst-content .wy-alert-info.caution .wy-alert-title,
+.rst-content .wy-alert-info.danger .admonition-title,
+.rst-content .wy-alert-info.danger .wy-alert-title,
+.rst-content .wy-alert-info.error .admonition-title,
+.rst-content .wy-alert-info.error .wy-alert-title,
+.rst-content .wy-alert-info.hint .admonition-title,
+.rst-content .wy-alert-info.hint .wy-alert-title,
+.rst-content .wy-alert-info.important .admonition-title,
+.rst-content .wy-alert-info.important .wy-alert-title,
+.rst-content .wy-alert-info.tip .admonition-title,
+.rst-content .wy-alert-info.tip .wy-alert-title,
+.rst-content .wy-alert-info.warning .admonition-title,
+.rst-content .wy-alert-info.warning .wy-alert-title,
+.rst-content .wy-alert.wy-alert-info .admonition-title,
+.wy-alert.wy-alert-info .rst-content .admonition-title,
+.wy-alert.wy-alert-info .wy-alert-title {
+    background: #54c7ec;
+}
+
+.rst-content .admonition-todo,
+.rst-content .attention,
+.rst-content .caution,
+.rst-content .warning,
+.rst-content .wy-alert-warning.admonition,
+.rst-content .wy-alert-warning.danger,
+.rst-content .wy-alert-warning.error,
+.rst-content .wy-alert-warning.hint,
+.rst-content .wy-alert-warning.important,
+.rst-content .wy-alert-warning.note,
+.rst-content .wy-alert-warning.seealso,
+.rst-content .wy-alert-warning.tip,
+.wy-alert.wy-alert-warning {
+    background: #f3f4f7;
+}
+
+.rst-content .admonition-todo .admonition-title,
+.rst-content .admonition-todo .wy-alert-title,
+.rst-content .attention .admonition-title,
+.rst-content .attention .wy-alert-title,
+.rst-content .caution .admonition-title,
+.rst-content .caution .wy-alert-title,
+.rst-content .warning .admonition-title,
+.rst-content .warning .wy-alert-title,
+.rst-content .wy-alert-warning.admonition .admonition-title,
+.rst-content .wy-alert-warning.admonition .wy-alert-title,
+.rst-content .wy-alert-warning.danger .admonition-title,
+.rst-content .wy-alert-warning.danger .wy-alert-title,
+.rst-content .wy-alert-warning.error .admonition-title,
+.rst-content .wy-alert-warning.error .wy-alert-title,
+.rst-content .wy-alert-warning.hint .admonition-title,
+.rst-content .wy-alert-warning.hint .wy-alert-title,
+.rst-content .wy-alert-warning.important .admonition-title,
+.rst-content .wy-alert-warning.important .wy-alert-title,
+.rst-content .wy-alert-warning.note .admonition-title,
+.rst-content .wy-alert-warning.note .wy-alert-title,
+.rst-content .wy-alert-warning.seealso .admonition-title,
+.rst-content .wy-alert-warning.seealso .wy-alert-title,
+.rst-content .wy-alert-warning.tip .admonition-title,
+.rst-content .wy-alert-warning.tip .wy-alert-title,
+.rst-content .wy-alert.wy-alert-warning .admonition-title,
+.wy-alert.wy-alert-warning .rst-content .admonition-title,
+.wy-alert.wy-alert-warning .wy-alert-title {
+    background: #e94f3b;
+}
+
+
+html.writer-html5 .rst-content table.docutils th {
+    border: none;
+}
+
+html,
+body,
+.wy-body-for-nav,
+.wy-nav-content {
+    background: #ffffff !important;
+}
+
+
+.wy-nav-content-wrap {
+    background: none;
+}
\ No newline at end of file
diff --git a/docs/_templates/class.rst b/docs/_templates/class.rst
new file mode 100644
index 0000000..61375f4
--- /dev/null
+++ b/docs/_templates/class.rst
@@ -0,0 +1,11 @@
+.. role:: hidden
+    :class: hidden-section
+.. currentmodule:: {{ module }}
+
+
+{{ name | underline}}
+
+.. autoclass:: {{ name }}
+    :members:
+    :special-members:
+    :exclude-members: __weakref__, __init__
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index 8651e78..53a2148 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,59 +1,71 @@
 # Configuration file for the Sphinx documentation builder.
 #
 # This file only contains a selection of the most common options. For a full
 # list see the documentation:
 # https://www.sphinx-doc.org/en/master/usage/configuration.html
 
 # -- Path setup --------------------------------------------------------------
 
 # 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('../'))
 
 
 # -- Project information -----------------------------------------------------
 
 project = 'HDTorch'
 copyright = '2022, William Simon, Una Pale'
 author = 'William Simon, Una Pale'
 
 # The full version, including alpha/beta/rc tags
 release = '1.0.0'
 
 
 # -- 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.githubpages",
     "sphinx.ext.napoleon",
     "sphinx.ext.autodoc",
     "sphinx.ext.autosummary",
     "sphinx.ext.viewcode",
     "sphinx_rtd_theme",]
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # This pattern also affects html_static_path and html_extra_path.
 exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
 
 
 # -- Options for HTML output -------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-#
-html_theme = 'alabaster'
+
+html_theme = "sphinx_rtd_theme"
+html_theme_path = ["_themes"]
+
+html_theme_options = {
+    "logo_only": True,
+    "display_version": True,
+    "prev_next_buttons_location": "bottom",
+    "style_nav_header_background": "white",
+}
 
 # 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']
\ No newline at end of file
+html_static_path = ["_static"]
+
+html_css_files = [
+    "css/custom.css",
+]
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 3d3964b..7a15873 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,13 +1,13 @@
-Welcome to the Torchhd documentation!
+Welcome to the HDTorch documentation!
 =====================================
 
-Torchhd is a Python library dedicated to *Hyperdimensional Computing* (also knwon as *Vector Symbolic Architectures*).
+
 
 .. toctree::
    :glob:
    :maxdepth: 1
    :caption: Tutorials
 
    getting_started
    util
    model
\ No newline at end of file
diff --git a/docs/model.rst b/docs/model.rst
index fd10da6..4d4a4a7 100644
--- a/docs/model.rst
+++ b/docs/model.rst
@@ -1,17 +1,15 @@
 .. _model:
 
-HDTorch model class
-======================
+HDTorch Base Classifier
+=======================
 
 .. currentmodule:: hdtorch.model
 
-This module consists of the basic hypervector generation functions and operations used on hypervectors.
+The HD_classifier class is the basis on which other HD classifiers can be built, and serves as a vehicle for demonstrating HDTorch's functionality.
 
-Basis-hypervector sets
-----------------------------------
 
 .. autosummary::
     :toctree: generated/
     :template: class.rst
 
     HD_classifier
\ No newline at end of file
diff --git a/hdtorch/model.py b/hdtorch/model.py
index f10b7a3..4f98a7d 100644
--- a/hdtorch/model.py
+++ b/hdtorch/model.py
@@ -1,126 +1,182 @@
 import torch
 import math
 from . import _hdtorchcuda
 from .hyperdimGenerators import generateHDVector
 from .util import rotateVec, xor_bipolar
 
+__all__ = [
+    "HD_classifier",
+]
 
 class HD_classifier:
     """Base HD classifier with generic parameters and learning and prediction funtions
     
+    Creates a simple HD classifier model with learning and prediction functions
+    
+    Args:
+        HDParams: an HDParams object that initializes the classifier
     """
 
     def __init__(self, HDParams):
 
         self.numFeat      = HDParams.numFeat
         self.D            = HDParams.D
         self.packed       = HDParams.packed
         self.device       = HDParams.device
         self.bindingStrat = HDParams.bindingStrat
         self.HDFlavor     = HDParams.HDFlavor
         
         if self.HDFlavor == 'bipol' and self.packed:
             raise TypeError('Model cannot be bipol and packed simultanously')
             
         if self.bindingStrat == 'FeatAppend':
             self.bindingFunction = lambda d : self.featureLevelValues[d].view(d.shape[0],1,-1)
         elif self.bindingStrat == 'FeatPermute':
             self.bindingFunction = lambda d : rotateVec(self.featureIDs, d)
         elif self.bindingStrat == 'IDLevelEncoding' and self.HDFlavor == 'binary':
             self.bindingFunction = lambda d : torch.bitwise_xor(self.featureIDs.unsqueeze(0), self.featureLevelValues[d])            
         elif self.bindingStrat == 'IDLevelEncoding' and self.HDFlavor == 'bipol' and not self.packed:
             self.bindingFunction = lambda d : xor_bipolar(self.featureIDs.unsqueeze(0), self.featureLevelValues[d])
         else:
             raise TypeError(f'Unrecognized combination of bindingStrat: {self.bindingStrat}, HDFlavor: {self.HDFlavor}, packed: {self.packed}')
         
         self.modelVectors = torch.zeros((HDParams.numClasses, self.D), device=self.device)
         if self.packed:
             self.modelVectorsNorm= torch.zeros((HDParams.numClasses, math.ceil(self.D/32)), device = self.device, dtype=torch.int32)
         else:
             self.modelVectorsNorm= torch.zeros((HDParams.numClasses, self.D), device = self.device, dtype=torch.int8)
             
         self.numAddedVecPerClass = torch.zeros(HDParams.numClasses, device=self.device, dtype=torch.int32)
         self.distanceFunction = self.ham_dist_arr if HDParams.similarityType=='hamming' else self.cos_dist_arr 
         
         self.featureIDs = generateHDVector(HDParams.IDVecType,self.numFeat,HDParams.D,self.packed,self.device)
         self.featureIDs[self.featureIDs==0] = -1 if self.HDFlavor=='bipol' else 0
         
         self.featureLevelValues = generateHDVector(HDParams.levelVecType,HDParams.numSegmentationLevels,HDParams.D,self.packed,self.device)
         self.featureLevelValues[self.featureLevelValues==0] = -1 if self.HDFlavor=='bipol' else 0
         
         
     def learn_HD_projections(self, data):
-        ''' From features of one window to HDvector representing that data window
-        '''
+        """ Project training data into HD space via selected binding function
+        
+        learn_HD_projections is expecting data to be discretized into a range of values equal to the number of values present in
+        in the featureLevelValues array. The input data will be used to index featureLevelValues, with the return value being
+        bound to the featureID corresponding to the feature.
+        
+        Args:
+            data: 2D array of discretized training
+        
+        Returns:
+            The projected and bound features in uncompressed binary form, with each bit having type int8, and, if the user specified 
+            packed mode when initializing the HD_classifier, the packed projections.
+        """
         outputVector = torch.empty((data.shape[0],self.D),dtype = torch.int8, device = data.device)
         if self.packed:
             b = self.bindingFunction(data)
             a = _hdtorchcuda.vcount(self.bindingFunction(data),self.D)
         else:
             a = self.bindingFunction(data).sum(1,dtype=torch.int16)
         if self.bindingStrat != 'FeatAppend':
             f =  self.numFeat>>1 if self.HDFlavor=='binary' else 0
             torch.gt(a,f,out=outputVector)
         else:
             outputVector = a.to(torch.int8)
         
         if self.HDFlavor == 'bipol':
             outputVector[outputVector==0] = -1
 
         if self.packed:
             packedOutput = torch.full((data.shape[0],math.ceil(self.D/32)),-1,dtype = torch.int32, device = data.device)
             _hdtorchcuda.pack(outputVector,packedOutput)
         else:
             packedOutput = -1
 
         return outputVector, packedOutput
     
     def ham_dist_arr(self, vec_a, vec_b):
-        ''' calculate relative hamming distance for for np array'''
+        """Calculate relative hamming distance between two hypervectors
+        
+        ham_dist_arr will perform a bitwise xor if the two hypervectors are in binary form, else it will compare their values and
+        return 1 if the values are different.
+        
+        Args:
+            vec_a: First hypervectors to be compared.
+            vec_b: Second hypervectors to be compared.
+        Returns:
+            The count of bits in the hypervectors that are different divided by the hypervectors size.
+        """
         vec_c = torch.bitwise_xor(vec_a,vec_b) if self.HDFlavor == 'binary' else vec_a != vec_b
         return _hdtorchcuda.hcount(vec_c)/float(self.D) if self.packed else vec_c.sum(2,dtype=torch.int16)/float(self.D)
     
     def cos_dist_arr(self, vA, vB):
-        ''' calculate cosine distance of two vectors'''
+        """Calculate cosine distance distance between two hypervectors
+        
+        cos_dist_arr currently only works with unpacked hypervectors.
+        
+        Args:
+            vec_a: First hypervectors to be compared.
+            vec_b: Second hypervectors to be compared.
+        Returns:
+            The cosine distance between the input hypervectors.
+        """
         output = np.dot(vA, vB) / (np.sqrt(np.dot(vA,vA)) * np.sqrt(np.dot(vB,vB)))
         outTensor = torch.tensor(1.0-output) #because we use later that if value is 0 then vectors are the same
         return outTensor
     
-    def givePrediction(self,data, encoded=False):
-        if not encoded:
+    def givePrediction(self, data, projected=False):
+        """Predict class to which input data belongs
+        
+        If the input data has not been projected into HD space, project data. Then, compare data with class vectors 
+        via the selected distance function and return the closest class.
+        
+        Args:
+            data: Array of data for which to predict class. My be already projected into HD space or not.
+            projected: Boolean value indicating if data has been already projected into HD space.
+        Returns:
+            A vector of predicted classes, and the distance from each classes for each projected data point.
+        """
+        if not projected:
             (temp, tempPacked) = self.learn_HD_projections(data)
             temp = temp if not self.packed else tempPacked
             data = temp.view(-1,1,temp.shape[-1])
         else:
             data = data.view(-1,1,data.shape[-1])
         distances = self.distanceFunction(data, self.modelVectorsNorm.unsqueeze(0))
         #find minimum
         minVal = torch.argmin(distances,-1)
 
         return (minVal.squeeze(), distances.squeeze())
     
     def trainModelVecOnData(self, data, labels):
-        '''learn model vectors for single pass 2 class HD learning '''
+        """Learn HD class vectors for from training data and labels.
+        
+        trainModelVecOnData will project each data point into HD space, bind it with the feature hypervector,
+        then bundle all bound hypervectors of the same class into a class vector.
+        
+        Args:
+            data: Array of data from which to learn class vectors
+            labels: Array of labels from which to learn class vectors
+        """
         # number of clases
         numLabels = self.modelVectors.shape[0]
     
         # Encode data
         (temp, packedTemp) = self.learn_HD_projections(data)
         temp = temp if not self.packed else packedTemp
         
         #go through all data windows and add them to model vectors
         for l in range(numLabels):
             t = temp[labels==l,:]
             if(t.numel() != 0 and self.packed):
                 self.modelVectors[l, :] += _hdtorchcuda.vcount(temp[labels==l],self.D) #temp[labels==l].sum(0)
             elif not self.packed:
                 self.modelVectors[l] += temp[labels==l].sum(0)
             self.numAddedVecPerClass[l] +=  (labels==l).sum().cpu() #count number of vectors added to each subclass
     
         # normalize model vectors to be binary (or bipolar) again
         if self.HDFlavor == 'binary' and self.packed:
                 _hdtorchcuda.pack(self.modelVectors > (self.numAddedVecPerClass.unsqueeze(1)>>1),self.modelVectorsNorm)
         elif self.HDFlavor == 'binary' and not self.packed:
                 self.modelVectorsNorm = self.modelVectors > (self.numAddedVecPerClass.unsqueeze(1)>>1)
         elif self.HDFlavor == 'bipol':
             self.modelVectorsNorm = torch.where(self.modelVectors>0,1,-1)