Repository: NSAPH-Software/pycausalgps (language: python)

Issue:
update tests



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
index 870478e..3f6e98c 100644
--- a/.github/workflows/python-app.yml
+++ b/.github/workflows/python-app.yml
@@ -30,7 +30,7 @@ jobs:
         sudo apt-get install -y r-base
     - name: Install R packages
       run: |
-        sudo R -e 'install.packages(c("polycor", "locpol", "wCorr"), repos="https://cran.rstudio.com/")'
+        sudo R -e 'install.packages(c("polycor", "locpol", "wCorr", "gam", "gnm"), repos="https://cran.rstudio.com/")'
     - name: Setup conda
       uses: s-weigand/setup-conda@v1
     - name: Install dependencies
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0778f92..5340aa9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,16 +3,10 @@
 See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) for more details.
 
 
-## [Unreleased]
-### Added
-
-- Database class to handle database connection and queries.
+## pycafe 0.0.3 - 2022-06-07
 
-### Changed
-- 
-
-### Fixed 
-- 
+### Added
 
-### Removed
-- 
\ No newline at end of file
+- Exposure response function for parametric, semi-parametric and non-parametric models.
+- Estimation of pseudo-population using weighting and matching approaches.
+- Estimation of GPS using normal and kernel approaches. 
diff --git a/README.md b/README.md
index 984a9f6..59c0bb8 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,7 @@
 # pycausalgps
 
-_[This packages is under development. Some of the features are not yet implemented.]_
-
 [![Test Status](https://github.com/nsaph-software/pycausalgps/workflows/Python%20package/badge.svg?branch=develop&event=push)](https://github.com/nsaph-software/pycausalgps/actions)
-[![PyPI version](https://badge.fury.io/py/pycausalgps.svg)](https://badge.fury.io/py/pycausalgps)
+[![PyPI version](https://img.shields.io/pypi/v/pycausalgps.svg)](https://pypi.org/project/pycausalgps)
 [![codecov](https://codecov.io/gh/NSAPH-Software/pycausalgps/branch/develop/graph/badge.svg)](https://codecov.io/gh/NSAPH-Software/pycausalgps)
 
 
@@ -31,6 +29,175 @@ git clone https://github.com/NSAPH-Software/pycausalgps
 cd pycausalgps
 pip install .
 ```
+## Usage
+
+### Geneting synthetic data
+
+```python
+from pycausalgps.base.utils import generate_syn_pop
+data = generate_syn_pop(sample_size=1000, 
+                        seed_val=456, 
+                        outcome_sd=0.25, 
+                        gps_spec=1, 
+                        cova_spec=2)
+```
+
+### Estimating GPS
+
+```python
+from pycausalgps.base.utils import generate_syn_pop
+from pycausalgps.base import GeneralizedPropensityScore
+
+params = {"gps_density": "normal",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                            }
+                }
+}
+
+data = generate_syn_pop(sample_size=1000, 
+                        seed_val=456, 
+                        outcome_sd=0.25, 
+                        gps_spec=1, 
+                        cova_spec=2)
+
+gps = GeneralizedPropensityScore(data, params)
+```
+
+### Generating Pseudo Population
+
+- **Weighting approach**
+
+```python
+from pycausalgps.pseudo_population import PseudoPopulation
+
+
+gps_params = {"gps_density": "normal",
+              "exposure_column": "treat",
+              "covariate_column_num": ["cf1", 
+                                       "cf2", 
+                                       "cf3", 
+                                       "cf4", 
+                                       "cf6"],
+              "covariate_column_cat": ["cf5"],
+              "libs":{
+                      "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+}
+
+data = generate_syn_pop(sample_size=1000, 
+                        seed_val=456, 
+                        outcome_sd=0.25, 
+                        gps_spec=1, 
+                        cova_spec=2)
+    
+gps = GeneralizedPropensityScore(data, gps_params)
+
+results = gps.get_results()
+gps_data = {
+    'data' : results.get("data"),
+    'gps_density' : results.get("gps_density")
+}
+
+
+
+pspop_params = {"approach" : "weighting", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                         "cf2", 
+                                         "cf3", 
+                                         "cf4", 
+                                         "cf6"],
+                "covariate_column_cat": ["cf5"],}
+
+pspop = PseudoPopulation(data=data, 
+                         gps_data=gps_data, 
+                         params=pspop_params)
+
+```
+
+- **Matching approach**
+
+```python
+from pycausalgps.base.utils import generate_syn_pop
+from pycausalgps.gps import GeneralizedPropensityScore
+from pycausalgps.pseudo_population import PseudoPopulation
+
+
+gps_params = {"gps_density": "normal",
+              "exposure_column": "treat",
+              "covariate_column_num": ["cf1", 
+                                       "cf2", 
+                                       "cf3", 
+                                       "cf4", 
+                                       "cf6"],
+              "covariate_column_cat": ["cf5"],
+              "libs":{
+                      "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+}
+
+data = generate_syn_pop(sample_size=500, 
+                        seed_val=456, 
+                        outcome_sd=0.25, 
+                        gps_spec=1, 
+                        cova_spec=2)
+    
+gps = GeneralizedPropensityScore(data, gps_params)
+
+results = gps.get_results()
+gps_data = {
+    'data' : results.get("data"),
+    'gps_density' : results.get("gps_density"),
+    'gps_minmax': results.get("gps_minmax")
+}
+
+
+
+pspop_params = {"approach" : "matching", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                         "cf2", 
+                                         "cf3", 
+                                         "cf4", 
+                                         "cf6"],
+                "covariate_column_cat": ["cf5"],
+                "control_params": {"caliper": 1.0,
+                                   "scale": 0.5,
+                                   "dist_measure": "l1",
+                                   "bin_seq": None},
+                "run_params": {"n_thread": 12,
+                               "chunk_size": 500},
+}
+
+pspop = PseudoPopulation(data=data, 
+                         gps_data=gps_data, 
+                         params=pspop_params)
+```
 
 ## Documentation
 
diff --git a/docs/CHANGELOG.html b/docs/CHANGELOG.html
index 781c29f..632fcbd 100644
--- a/docs/CHANGELOG.html
+++ b/docs/CHANGELOG.html
@@ -46,8 +46,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
@@ -61,11 +59,8 @@
 <ul class="current">
 <li class="toctree-l1"><a class="reference internal" href="contact.html">Contact Us</a></li>
 <li class="toctree-l1 current"><a class="current reference internal" href="#">Changelog</a><ul>
-<li class="toctree-l2"><a class="reference internal" href="#unreleased">[Unreleased]</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="#pycafe-0-0-3-2022-06-07">pycafe 0.0.3 - 2022-06-07</a><ul>
 <li class="toctree-l3"><a class="reference internal" href="#added">Added</a></li>
-<li class="toctree-l3"><a class="reference internal" href="#changed">Changed</a></li>
-<li class="toctree-l3"><a class="reference internal" href="#fixed">Fixed</a></li>
-<li class="toctree-l3"><a class="reference internal" href="#removed">Removed</a></li>
 </ul>
 </li>
 </ul>
@@ -123,30 +118,14 @@
 <div class="section" id="changelog">
 <h1>Changelog<a class="headerlink" href="#changelog" title="Permalink to this headline"></a></h1>
 <p>See <a class="reference external" href="https://keepachangelog.com/en/1.0.0/">Keep a Changelog</a> for more details.</p>
-<div class="section" id="unreleased">
-<h2>[Unreleased]<a class="headerlink" href="#unreleased" title="Permalink to this headline"></a></h2>
+<div class="section" id="pycafe-0-0-3-2022-06-07">
+<h2>pycafe 0.0.3 - 2022-06-07<a class="headerlink" href="#pycafe-0-0-3-2022-06-07" title="Permalink to this headline"></a></h2>
 <div class="section" id="added">
 <h3>Added<a class="headerlink" href="#added" title="Permalink to this headline"></a></h3>
 <ul class="simple">
-<li><p>Database class to handle database connection and queries.</p></li>
-</ul>
-</div>
-<div class="section" id="changed">
-<h3>Changed<a class="headerlink" href="#changed" title="Permalink to this headline"></a></h3>
-<ul class="simple">
-<li></li>
-</ul>
-</div>
-<div class="section" id="fixed">
-<h3>Fixed<a class="headerlink" href="#fixed" title="Permalink to this headline"></a></h3>
-<ul class="simple">
-<li></li>
-</ul>
-</div>
-<div class="section" id="removed">
-<h3>Removed<a class="headerlink" href="#removed" title="Permalink to this headline"></a></h3>
-<ul class="simple">
-<li></li>
+<li><p>Exposure response function for parametric, semi-parametric and non-parametric models.</p></li>
+<li><p>Estimation of pseudo-population using weighting and matching approaches.</p></li>
+<li><p>Estimation of GPS using normal and kernel approaches.</p></li>
 </ul>
 </div>
 </div>
diff --git a/docs/_modules/index.html b/docs/_modules/index.html
index 6f7b0b6..f6b2e84 100644
--- a/docs/_modules/index.html
+++ b/docs/_modules/index.html
@@ -45,8 +45,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="../setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="../quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="../faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
diff --git a/docs/_modules/pycausalgps/gps.html b/docs/_modules/pycausalgps/gps.html
index 7ec7be5..56cdb30 100644
--- a/docs/_modules/pycausalgps/gps.html
+++ b/docs/_modules/pycausalgps/gps.html
@@ -45,9 +45,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="../../setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="../../quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../../gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../../gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../../faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -114,19 +111,60 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
 
 <span class="sd">    Parameters</span>
 <span class="sd">    ----------</span>
+
 <span class="sd">    data: pd.DataFrame</span>
 <span class="sd">        A pandas DataFrame that contains the data for the GPS computation.</span>
 <span class="sd">    params : dict</span>
-<span class="sd">        A dictionary of gps parameters.</span>
+<span class="sd">        A dictionary of gps parameters. This includes the folowing required</span>
+<span class="sd">        parameters:</span>
+
+<span class="sd">            - gps_density: str</span>
+<span class="sd">                A string that indicates the density estimation method to be</span>
+<span class="sd">                used. Available options are &quot;normal&quot; and &quot;kernel&quot;.</span>
+<span class="sd">            - exposure_column: str  </span>
+<span class="sd">                A string that indicates the name of the exposure column.</span>
+<span class="sd">            - covariate_column_num: list</span>
+<span class="sd">                A list of strings that indicates the names of the numerical</span>
+<span class="sd">                covariate columns.</span>
+<span class="sd">            - covariate_column_cat: list</span>
+<span class="sd">                A list of strings that indicates the names of the categorical</span>
+<span class="sd">                covariate columns.</span>
+<span class="sd">            - libs: dict</span>
+<span class="sd">                A dictionary of libraries and their parameters. Currently only</span>
+<span class="sd">                xgboost is supported. Any parameters for hypertuning the </span>
+<span class="sd">                xgboost model can be passed including:</span>
+
+<span class="sd">                    - n_estimators: int</span>
+<span class="sd">                        Number of trees to fit.</span>
+<span class="sd">                    - learning_rate: float</span>
+<span class="sd">                        Learning rate for the xgboost model.</span>
+<span class="sd">                    - max_depth: int</span>
+<span class="sd">                        Maximum depth of a tree.</span>
+<span class="sd">                    - test_rate: float</span>
+<span class="sd">                        The proportion of the data to be used for testing.</span>
+<span class="sd">                    - random_state: int</span>
+<span class="sd">                        Random state for the xgboost model.</span>
+
+<span class="sd">    Returns</span>
+<span class="sd">    -------</span>
+
+<span class="sd">    GeneralizedPropensityScore object that includes the following attributes:</span>
+<span class="sd">        - data: pd.DataFrame</span>
+<span class="sd">            The gps and auxilary columns.</span>
+<span class="sd">        - gps_minmax: list</span>
+<span class="sd">            A list of the minimum and maximum gps values.</span>
+<span class="sd">        - training_report: dict</span>
+<span class="sd">            A dictionary of the training report for the xgboost model.</span>
 <span class="sd">    &quot;&quot;&quot;</span>
     
     <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">,</span> <span class="n">params</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
-
         <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
         <span class="bp">self</span><span class="o">.</span><span class="n">params</span> <span class="o">=</span> <span class="n">params</span>
         <span class="bp">self</span><span class="o">.</span><span class="n">training_report</span> <span class="o">=</span> <span class="p">{}</span>
         <span class="bp">self</span><span class="o">.</span><span class="n">results</span> <span class="o">=</span> <span class="kc">None</span>
-        <span class="bp">self</span><span class="o">.</span><span class="n">compute_gps</span><span class="p">()</span>
+        <span class="bp">self</span><span class="o">.</span><span class="n">_check_data</span><span class="p">()</span>
+        <span class="bp">self</span><span class="o">.</span><span class="n">_check_params</span><span class="p">()</span>
+        <span class="bp">self</span><span class="o">.</span><span class="n">_compute_gps</span><span class="p">()</span>
 
     <span class="nd">@property</span>
     <span class="k">def</span> <span class="nf">data</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">:</span>
@@ -155,6 +193,27 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
     <span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
         <span class="k">return</span> <span class="p">(</span><span class="sa">f</span><span class="s2">&quot;GeneralizedPropensityScore(data=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="si">}</span><span class="s2">,&quot;</span>
                 <span class="sa">f</span><span class="s2">&quot;params=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
+    
+
+<div class="viewcode-block" id="GeneralizedPropensityScore._check_data"><a class="viewcode-back" href="../../modules.html#pycausalgps.gps.GeneralizedPropensityScore._check_data">[docs]</a>    <span class="k">def</span> <span class="nf">_check_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+        <span class="sd">&quot;&quot;&quot;</span>
+<span class="sd">        Check if the data is in the correct format.</span>
+<span class="sd">        &quot;&quot;&quot;</span>
+        <span class="c1">#TODO: add more checks</span>
+        <span class="k">pass</span></div>
+
+<div class="viewcode-block" id="GeneralizedPropensityScore._check_params"><a class="viewcode-back" href="../../modules.html#pycausalgps.gps.GeneralizedPropensityScore._check_params">[docs]</a>    <span class="k">def</span> <span class="nf">_check_params</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+        <span class="sd">&quot;&quot;&quot;</span>
+<span class="sd">        Check if the params are in the correct format.</span>
+<span class="sd">        &quot;&quot;&quot;</span>
+
+        <span class="n">required_params</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;gps_density&quot;</span><span class="p">,</span> <span class="s2">&quot;exposure_column&quot;</span><span class="p">,</span> 
+                           <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">,</span> <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">,</span> 
+                           <span class="s2">&quot;libs&quot;</span><span class="p">]</span>
+        
+        <span class="k">for</span> <span class="n">param</span> <span class="ow">in</span> <span class="n">required_params</span><span class="p">:</span>
+            <span class="k">if</span> <span class="ow">not</span> <span class="n">param</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
+                <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Required parameter </span><span class="si">{</span><span class="n">param</span><span class="si">}</span><span class="s2"> is missing.&quot;</span><span class="p">)</span></div>
 
 <div class="viewcode-block" id="GeneralizedPropensityScore.compute_gps"><a class="viewcode-back" href="../../modules.html#pycausalgps.gps.GeneralizedPropensityScore.compute_gps">[docs]</a>    <span class="k">def</span> <span class="nf">compute_gps</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
         <span class="sd">&quot;&quot;&quot;</span>
@@ -162,8 +221,7 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
 <span class="sd">        parameters.</span>
 <span class="sd">        &quot;&quot;&quot;</span>
 
-        <span class="n">libs</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span>
-                          <span class="p">[</span><span class="s2">&quot;gps_params&quot;</span><span class="p">,</span> <span class="s2">&quot;libs&quot;</span><span class="p">])</span>
+        <span class="n">libs</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;libs&quot;</span><span class="p">])</span>
         
         <span class="n">first_level_keys</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">libs</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span>
         
@@ -183,21 +241,30 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
             
             <span class="n">gps_min</span><span class="p">,</span> <span class="n">gps_max</span> <span class="o">=</span> <span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;gps&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">min</span><span class="p">(),</span> <span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;gps&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">max</span><span class="p">()</span>
             <span class="n">gps_standardized</span> <span class="o">=</span> <span class="p">(</span><span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;gps&quot;</span><span class="p">]</span> <span class="o">-</span> <span class="n">gps_min</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">gps_max</span> <span class="o">-</span> <span class="n">gps_min</span><span class="p">)</span>    
-            <span class="bp">self</span><span class="o">.</span><span class="n">results</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;data&quot;</span><span class="p">:</span><span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
+            <span class="n">results</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;data&quot;</span><span class="p">:</span><span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
                                        <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="s2">&quot;id&quot;</span><span class="p">],</span>
                                         <span class="s2">&quot;gps&quot;</span><span class="p">:</span> <span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;gps&quot;</span><span class="p">],</span>
                                         <span class="s2">&quot;gps_standardized&quot;</span><span class="p">:</span> <span class="n">gps_standardized</span><span class="p">,</span>
                                         <span class="s2">&quot;e_gps_pred&quot;</span><span class="p">:</span> <span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;e_gps_pred&quot;</span><span class="p">],</span>
                                         <span class="s2">&quot;e_gps_std_pred&quot;</span><span class="p">:</span> <span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;e_gps_std&quot;</span><span class="p">],</span>
                                         <span class="s2">&quot;w_resid&quot;</span><span class="p">:</span> <span class="n">gps_res</span><span class="p">[</span><span class="s2">&quot;w_resid&quot;</span><span class="p">]}),</span>
-                                <span class="s2">&quot;gps_minmax&quot;</span><span class="p">:[</span><span class="n">gps_min</span><span class="p">,</span> <span class="n">gps_max</span><span class="p">],</span>
-                                <span class="s2">&quot;training_report&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">training_report</span>
+                        <span class="s2">&quot;gps_minmax&quot;</span><span class="p">:[</span><span class="n">gps_min</span><span class="p">,</span> <span class="n">gps_max</span><span class="p">],</span>
+                        <span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">[</span><span class="s2">&quot;gps_density&quot;</span><span class="p">],</span>
+                        <span class="s2">&quot;training_report&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">training_report</span><span class="p">,</span>
             <span class="p">}</span>
-           
+            <span class="k">return</span> <span class="n">results</span>
+
         <span class="k">else</span><span class="p">:</span>
             <span class="n">LOGGER</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot; GPS computing approach (approach): &quot;</span>
-                           <span class="sa">f</span><span class="s2">&quot; </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">[</span><span class="s1">&#39;approach&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">  is not defined.&quot;</span><span class="p">)</span></div>
+                           <span class="sa">f</span><span class="s2">&quot; </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">[</span><span class="s1">&#39;approach&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">  is not defined.&quot;</span><span class="p">)</span>
+            <span class="k">return</span> <span class="kc">None</span></div>
+
 
+<div class="viewcode-block" id="GeneralizedPropensityScore._compute_gps"><a class="viewcode-back" href="../../modules.html#pycausalgps.gps.GeneralizedPropensityScore._compute_gps">[docs]</a>    <span class="k">def</span> <span class="nf">_compute_gps</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
+        <span class="sd">&quot;&quot;&quot;</span>
+<span class="sd">        Compute GPS based on the provided parameters.</span>
+<span class="sd">        &quot;&quot;&quot;</span>
+        <span class="bp">self</span><span class="o">.</span><span class="n">results</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">compute_gps</span><span class="p">()</span></div>
 
 <div class="viewcode-block" id="GeneralizedPropensityScore.compute_gps_xgboost"><a class="viewcode-back" href="../../modules.html#pycausalgps.gps.GeneralizedPropensityScore.compute_gps_xgboost">[docs]</a>    <span class="k">def</span> <span class="nf">compute_gps_xgboost</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
         <span class="sd">&quot;&quot;&quot;</span>
@@ -210,21 +277,15 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
         <span class="c1"># different parameters without using the class (provided that all information is given).</span>
         
         <span class="c1"># preprocess data   </span>
-        <span class="n">num_cols</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span>
-                              <span class="p">[</span><span class="s2">&quot;gps_params&quot;</span><span class="p">,</span> 
-                               <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">])</span>
+        <span class="n">num_cols</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;covariate_column_num&quot;</span><span class="p">])</span>
         
         <span class="n">X_num</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="n">num_cols</span><span class="p">]</span>
 
-        <span class="n">exposure_col</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span>
-                                    <span class="p">[</span><span class="s2">&quot;gps_params&quot;</span><span class="p">,</span>
-                                     <span class="s2">&quot;exposure_column&quot;</span><span class="p">])</span>
+        <span class="n">exposure_col</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;exposure_column&quot;</span><span class="p">])</span>
 
         <span class="n">y</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="n">exposure_col</span><span class="p">]</span>
  
-        <span class="n">cat_cols</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span>
-                              <span class="p">[</span><span class="s2">&quot;gps_params&quot;</span><span class="p">,</span>
-                               <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">])</span>
+        <span class="n">cat_cols</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">])</span>
 
         <span class="n">X_cat</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="n">cat_cols</span><span class="p">]</span>
         <span class="n">X_cat</span> <span class="o">=</span> <span class="n">X_cat</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
@@ -252,11 +313,10 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
         <span class="n">X_</span><span class="o">.</span><span class="n">loc</span><span class="p">[:,</span><span class="n">num_cols</span><span class="p">]</span> <span class="o">=</span> <span class="n">standard</span><span class="o">.</span><span class="n">transform</span><span class="p">(</span><span class="n">X_</span><span class="o">.</span><span class="n">loc</span><span class="p">[:,</span><span class="n">num_cols</span><span class="p">])</span>       
 
         <span class="c1"># get hyperparameters</span>
-        <span class="n">hyper_params</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span>
-                          <span class="p">[</span><span class="s2">&quot;gps_params&quot;</span><span class="p">,</span> <span class="s2">&quot;libs&quot;</span><span class="p">,</span> <span class="s2">&quot;xgboost&quot;</span><span class="p">])</span>
+        <span class="n">hyper_params</span> <span class="o">=</span> <span class="n">nested_get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;libs&quot;</span><span class="p">,</span> <span class="s2">&quot;xgboost&quot;</span><span class="p">])</span>
 
 
-        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_params&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;model&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;parametric&quot;</span><span class="p">:</span>
+        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_density&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;normal&quot;</span><span class="p">:</span>
 
             <span class="n">e_gps_pred</span><span class="p">,</span> <span class="n">training_report</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">xgb_train_it</span><span class="p">(</span><span class="n">X_</span><span class="p">,</span> 
                                                             <span class="n">y</span><span class="o">.</span><span class="n">squeeze</span><span class="p">(),</span> 
@@ -270,7 +330,7 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
                         <span class="n">e_gps_std</span><span class="o">=</span><span class="n">e_gps_std</span><span class="p">,</span>
                         <span class="n">w_resid</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
 
-        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_params&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;model&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;non-parametric&quot;</span><span class="p">:</span>
+        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_density&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;kernel&quot;</span><span class="p">:</span>
 
             <span class="n">e_gps_pred</span><span class="p">,</span> <span class="n">training_report</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">xgb_train_it</span><span class="p">(</span><span class="n">X_</span><span class="p">,</span>
                                                             <span class="n">y</span><span class="o">.</span><span class="n">squeeze</span><span class="p">(),</span> 
@@ -295,9 +355,9 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
                         <span class="n">e_gps_std</span><span class="o">=</span><span class="n">e_gps_std_pred</span><span class="p">,</span> 
                         <span class="n">w_resid</span><span class="o">=</span><span class="n">w_resid</span><span class="p">)</span>
         <span class="k">else</span><span class="p">:</span>
-            <span class="n">LOGGER</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;gps_model: &#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">[</span><span class="s1">&#39;gps_model&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#39;&quot;</span>
+            <span class="n">LOGGER</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;gps_density: &#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">params</span><span class="p">[</span><span class="s1">&#39;gps_density&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#39;&quot;</span>
                            <span class="sa">f</span><span class="s2">&quot; is not defined.&quot;</span>
-                           <span class="sa">f</span><span class="s2">&quot; Available models: parametric, non-parametric.&quot;</span><span class="p">)</span>
+                           <span class="sa">f</span><span class="s2">&quot; Available options: normal, kernel.&quot;</span><span class="p">)</span>
             <span class="k">return</span> <span class="nb">dict</span><span class="p">()</span></div>
 
 <div class="viewcode-block" id="GeneralizedPropensityScore.xgb_train_it"><a class="viewcode-back" href="../../modules.html#pycausalgps.gps.GeneralizedPropensityScore.xgb_train_it">[docs]</a>    <span class="nd">@staticmethod</span>
@@ -331,6 +391,7 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
         <span class="n">test_size</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;test_rate&quot;</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">)</span>
         <span class="n">max_depth</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;max_depth&quot;</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
         <span class="n">random_state</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;random_state&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">)</span>
+        <span class="n">n_jobs</span> <span class="o">=</span> <span class="n">params</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;n_jobs&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
 
         <span class="n">LOGGER</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;XGBoost used parameters: n_estimators=</span><span class="si">{</span><span class="n">n_estimators</span><span class="si">}</span><span class="s2">, &quot;</span>
                      <span class="sa">f</span><span class="s2">&quot;learning_rate=</span><span class="si">{</span><span class="n">learning_rate</span><span class="si">}</span><span class="s2">, test_size=</span><span class="si">{</span><span class="n">test_size</span><span class="si">}</span><span class="s2">, &quot;</span>
@@ -339,7 +400,8 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
 
         <span class="n">xgb</span> <span class="o">=</span> <span class="n">XGBRegressor</span><span class="p">(</span><span class="n">n_estimators</span> <span class="o">=</span> <span class="n">n_estimators</span><span class="p">,</span>
                            <span class="n">learning_rate</span> <span class="o">=</span> <span class="n">learning_rate</span><span class="p">,</span>
-                           <span class="n">max_depth</span> <span class="o">=</span> <span class="n">max_depth</span><span class="p">)</span>                 
+                           <span class="n">max_depth</span> <span class="o">=</span> <span class="n">max_depth</span><span class="p">,</span>
+                           <span class="n">n_jobs</span><span class="o">=</span><span class="n">n_jobs</span><span class="p">)</span>                 
         
         <span class="n">X_train</span><span class="p">,</span> <span class="n">X_val</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span> <span class="n">y_val</span> <span class="o">=</span> <span class="n">train_test_split</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span>
                                     <span class="n">test_size</span> <span class="o">=</span> <span class="n">test_size</span><span class="p">,</span> 
@@ -370,10 +432,6 @@ <h1>Source code for pycausalgps.gps</h1><div class="highlight"><pre>
 <span class="sd">        &quot;&quot;&quot;</span>
         <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">results</span></div></div>
 
-<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
-
-    <span class="k">pass</span>
-
 
 
 
diff --git a/docs/_modules/pycausalgps/rscripts/rfunctions.html b/docs/_modules/pycausalgps/rscripts/rfunctions.html
index 880b2d4..f88e785 100644
--- a/docs/_modules/pycausalgps/rscripts/rfunctions.html
+++ b/docs/_modules/pycausalgps/rscripts/rfunctions.html
@@ -45,9 +45,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="../../../setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="../../../quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../../../gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../../../gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="../../../faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -93,6 +90,7 @@ <h1>Source code for pycausalgps.rscripts.rfunctions</h1><div class="highlight"><
 <span class="sd">Main module to define the R functions to be used in the package.</span>
 <span class="sd">&quot;&quot;&quot;</span>
 
+<span class="kn">import</span> <span class="nn">os</span>
 
 <span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
 <span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
@@ -108,15 +106,13 @@ <h1>Source code for pycausalgps.rscripts.rfunctions</h1><div class="highlight"><
 <span class="c1"># Check if function is defined</span>
 <span class="c1"># TODO: Check if there is a better way to fix the path.</span>
 <span class="k">if</span> <span class="s1">&#39;compute_density&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">:</span>
+    
     <span class="k">try</span><span class="p">:</span>
-        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="s1">&#39;&#39;&#39;</span>
-<span class="s1">            compute_density &lt;- function(x0, x1){</span>
-<span class="s1">                tmp_density &lt;- stats::density(x0, na.rm = TRUE)</span>
-<span class="s1">                dnst &lt;- stats::approx(tmp_density$x, tmp_density$y, xout = x1,</span>
-<span class="s1">                                      rule = 2)$y</span>
-<span class="s1">            return(dnst)</span>
-<span class="s1">           }</span>
-<span class="s1">        &#39;&#39;&#39;</span><span class="p">)</span>
+        <span class="n">r_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> 
+                                   <span class="s1">&#39;compute_density.r&#39;</span><span class="p">)</span>
+
+        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;source(&quot;</span><span class="si">{</span><span class="n">r_file_path</span><span class="si">}</span><span class="s1">&quot;)&#39;</span><span class="p">)</span>
+
     <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
         <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Error in loading the R function: compute_density&#39;</span><span class="p">)</span>
         <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
@@ -170,69 +166,12 @@ <h1>Source code for pycausalgps.rscripts.rfunctions</h1><div class="highlight"><
 <span class="c1"># Check if function is defined</span>
 <span class="k">if</span> <span class="s1">&#39;absolute_weighted_corr&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">:</span>
     <span class="k">try</span><span class="p">:</span>
-        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="s1">&#39;&#39;&#39;</span>
-<span class="s1">            absolute_weighted_corr_df &lt;- function(w,</span>
-<span class="s1">                                      vw,</span>
-<span class="s1">                                      c_num,</span>
-<span class="s1">                                      c_cat){</span>
-<span class="s1">  </span>
-<span class="s1">  </span>
-<span class="s1"> </span>
-<span class="s1">                # detect numeric columns</span>
-<span class="s1">                col_n &lt;- colnames(c_num)</span>
-<span class="s1">              </span>
-<span class="s1">                # detect factorial columns</span>
-<span class="s1">                col_f &lt;- colnames(c_cat)</span>
-<span class="s1">              </span>
-<span class="s1">                c_cat[] &lt;- lapply(c_cat, as.factor)</span>
-<span class="s1">              </span>
-<span class="s1">                absolute_corr_n &lt;- absolute_corr_f &lt;- NULL</span>
-<span class="s1">              </span>
-<span class="s1">                if (length(col_n) &gt; 0) {</span>
-<span class="s1">                  absolute_corr_n&lt;- sapply(col_n,function(i){</span>
-<span class="s1">                    abs(wCorr::weightedCorr(x = w,</span>
-<span class="s1">                                            y = c_num[,i],</span>
-<span class="s1">                                            weights = vw,</span>
-<span class="s1">                                            method = c(&quot;spearman&quot;)))})</span>
-<span class="s1">                  absolute_corr_n &lt;- unlist(absolute_corr_n)</span>
-<span class="s1">                  names(absolute_corr_n) &lt;- col_n</span>
-<span class="s1">                }</span>
-<span class="s1">              </span>
-<span class="s1">                if (length(col_f) &gt; 0) {</span>
-<span class="s1">                  internal_fun&lt;- function(i){</span>
-<span class="s1">                    abs(wCorr::weightedCorr(x = w,</span>
-<span class="s1">                                            y = c_cat[, i],</span>
-<span class="s1">                                            weights = vw,</span>
-<span class="s1">                                            method = c(&quot;Polyserial&quot;)))}</span>
-<span class="s1">              </span>
-<span class="s1">                  absolute_corr_f &lt;- c()</span>
-<span class="s1">                  for (item in col_f){</span>
-<span class="s1">                    if (length(unique(c_cat[[item]])) == 1 ){</span>
-<span class="s1">                      absolute_corr_f &lt;- c(absolute_corr_f, NA)</span>
-<span class="s1">                    } else {</span>
-<span class="s1">                      absolute_corr_f &lt;- c(absolute_corr_f, internal_fun(item))</span>
-<span class="s1">                    }</span>
-<span class="s1">                  }</span>
-<span class="s1">                  names(absolute_corr_f) &lt;- col_f</span>
-<span class="s1">                }</span>
-<span class="s1">              </span>
-<span class="s1">                absolute_corr &lt;- c(absolute_corr_n, absolute_corr_f)</span>
-<span class="s1">              </span>
-<span class="s1">                if (sum(is.na(absolute_corr)) &gt; 0){</span>
-<span class="s1">                  warning(paste(&quot;The following features generated missing values: &quot;,</span>
-<span class="s1">                                names(absolute_corr)[is.na(absolute_corr)],</span>
-<span class="s1">                                &quot;</span><span class="se">\n</span><span class="s1">In computing mean covariate balance, they will be ignored.&quot;))</span>
-<span class="s1">                }</span>
-<span class="s1">              </span>
-<span class="s1">                df &lt;- data.frame(name = character(), value = numeric())</span>
-<span class="s1">              </span>
-<span class="s1">                for (i in names(absolute_corr)){</span>
-<span class="s1">                  df &lt;- rbind(df, data.frame(name=i, value=absolute_corr[[i]]))</span>
-<span class="s1">                }</span>
-<span class="s1">              </span>
-<span class="s1">                return(df)</span>
-<span class="s1">            }</span>
-<span class="s1">        &#39;&#39;&#39;</span><span class="p">)</span>
+
+        <span class="n">r_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> 
+                                   <span class="s1">&#39;absolute_weighted_corr_df.r&#39;</span><span class="p">)</span>
+
+        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;source(&quot;</span><span class="si">{</span><span class="n">r_file_path</span><span class="si">}</span><span class="s1">&quot;)&#39;</span><span class="p">)</span>
+
     <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
         <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Error in loading the R function: absolute_weighted_corr&#39;</span><span class="p">)</span>
         <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
@@ -288,6 +227,143 @@ <h1>Source code for pycausalgps.rscripts.rfunctions</h1><div class="highlight"><
 <span class="c1">#### ---------------------------------------------------------------------------</span>
 
 
+<span class="c1">### Compute parametric exposure response function ------------------------------</span>
+
+<span class="c1"># R</span>
+<span class="c1"># TODO: Check if there is a better way to fix the path.</span>
+<span class="k">if</span> <span class="s1">&#39;r_estimate_pmetric_erf&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">:</span>
+    
+    <span class="k">try</span><span class="p">:</span>
+        <span class="n">r_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> 
+                                   <span class="s1">&#39;r_estimate_pmetric_erf.r&#39;</span><span class="p">)</span>
+
+        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;source(&quot;</span><span class="si">{</span><span class="n">r_file_path</span><span class="si">}</span><span class="s1">&quot;)&#39;</span><span class="p">)</span>
+
+    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Error in loading the R function: compute_density&#39;</span><span class="p">)</span>
+        <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
+
+
+<span class="c1"># Python</span>
+<span class="k">def</span> <span class="nf">estimate_pmetric_erf</span><span class="p">(</span><span class="n">formula</span><span class="p">:</span><span class="nb">str</span><span class="p">,</span>
+                         <span class="n">family</span><span class="p">:</span><span class="nb">str</span><span class="p">,</span>
+                         <span class="n">data</span><span class="p">:</span><span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">any</span><span class="p">:</span>
+    
+
+    <span class="c1"># Convert pandas dataframe to R dataframe</span>
+    <span class="k">with</span> <span class="n">localconverter</span><span class="p">(</span><span class="n">robjects</span><span class="o">.</span><span class="n">default_converter</span> <span class="o">+</span> <span class="n">pandas2ri</span><span class="o">.</span><span class="n">converter</span><span class="p">):</span>
+        <span class="n">r_data</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">conversion</span><span class="o">.</span><span class="n">py2rpy</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
+    
+    <span class="c1"># collect the function from R</span>
+    <span class="n">r_estimate_pmetric_erf</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">[</span><span class="s1">&#39;r_estimate_pmetric_erf&#39;</span><span class="p">]</span>
+
+    <span class="c1"># call the function</span>
+    <span class="n">results</span> <span class="o">=</span> <span class="n">r_estimate_pmetric_erf</span><span class="p">(</span><span class="n">formula</span><span class="p">,</span> 
+                                     <span class="n">family</span><span class="p">,</span> 
+                                     <span class="n">r_data</span><span class="p">)</span>
+    
+    <span class="c1"># Convert results to python</span>
+    <span class="k">with</span> <span class="n">localconverter</span><span class="p">(</span><span class="n">robjects</span><span class="o">.</span><span class="n">default_converter</span> <span class="o">+</span> 
+                        <span class="n">robjects</span><span class="o">.</span><span class="n">pandas2ri</span><span class="o">.</span><span class="n">converter</span><span class="p">):</span>
+        <span class="n">py_results</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">conversion</span><span class="o">.</span><span class="n">rpy2py</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
+
+    <span class="k">return</span> <span class="n">py_results</span>
+    
+<span class="c1">#### ---------------------------------------------------------------------------</span>
+
+<span class="c1">## Compute semiparametric exposure response function ---------------------------</span>
+
+<span class="c1"># R</span>
+<span class="c1"># TODO: Check if there is a better way to fix the path.</span>
+<span class="k">if</span> <span class="s1">&#39;r_estimate_semipmetric_erf&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">:</span>
+    
+    <span class="k">try</span><span class="p">:</span>
+        <span class="n">r_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> 
+                                   <span class="s1">&#39;r_estimate_semipmetric_erf.r&#39;</span><span class="p">)</span>
+
+        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;source(&quot;</span><span class="si">{</span><span class="n">r_file_path</span><span class="si">}</span><span class="s1">&quot;)&#39;</span><span class="p">)</span>
+
+    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Error in loading the R function: compute_density&#39;</span><span class="p">)</span>
+        <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
+
+
+<span class="c1"># Python</span>
+<span class="k">def</span> <span class="nf">estimate_semipmetric_erf</span><span class="p">(</span><span class="n">formula</span><span class="p">:</span><span class="nb">str</span><span class="p">,</span>
+                             <span class="n">family</span><span class="p">:</span><span class="nb">str</span><span class="p">,</span>
+                             <span class="n">data</span><span class="p">:</span><span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">any</span><span class="p">:</span>
+    
+
+    <span class="c1"># Convert pandas dataframe to R dataframe</span>
+    <span class="k">with</span> <span class="n">localconverter</span><span class="p">(</span><span class="n">robjects</span><span class="o">.</span><span class="n">default_converter</span> <span class="o">+</span> <span class="n">pandas2ri</span><span class="o">.</span><span class="n">converter</span><span class="p">):</span>
+        <span class="n">r_data</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">conversion</span><span class="o">.</span><span class="n">py2rpy</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
+    
+    <span class="c1"># collect the function from R</span>
+    <span class="n">r_estimate_semipmetric_erf</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">[</span><span class="s1">&#39;r_estimate_semipmetric_erf&#39;</span><span class="p">]</span>
+
+    <span class="c1"># call the function</span>
+    <span class="n">results</span> <span class="o">=</span> <span class="n">r_estimate_semipmetric_erf</span><span class="p">(</span><span class="n">formula</span><span class="p">,</span> 
+                                         <span class="n">family</span><span class="p">,</span> 
+                                         <span class="n">r_data</span><span class="p">)</span>
+    
+    <span class="c1"># Convert results to python</span>
+    <span class="k">with</span> <span class="n">localconverter</span><span class="p">(</span><span class="n">robjects</span><span class="o">.</span><span class="n">default_converter</span> <span class="o">+</span> 
+                        <span class="n">robjects</span><span class="o">.</span><span class="n">pandas2ri</span><span class="o">.</span><span class="n">converter</span><span class="p">):</span>
+        <span class="n">py_results</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">conversion</span><span class="o">.</span><span class="n">rpy2py</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
+
+    <span class="k">return</span> <span class="n">py_results</span>
+    
+<span class="c1">#### ---------------------------------------------------------------------------</span>
+
+
+<span class="c1">#### locpol function -----------------------------------------------------------</span>
+
+<span class="c1"># R</span>
+<span class="k">if</span> <span class="s1">&#39;r_locpol&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">:</span>
+        
+    <span class="k">try</span><span class="p">:</span>
+        <span class="n">r_file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> 
+                                <span class="s1">&#39;r_locpol.r&#39;</span><span class="p">)</span>
+
+        <span class="n">robjects</span><span class="o">.</span><span class="n">r</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;source(&quot;</span><span class="si">{</span><span class="n">r_file_path</span><span class="si">}</span><span class="s1">&quot;)&#39;</span><span class="p">)</span>
+
+    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
+        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Error in loading the R function: r_locpol&#39;</span><span class="p">)</span>
+        <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
+
+<span class="c1"># Python</span>
+
+<span class="k">def</span> <span class="nf">locpol</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">,</span> <span class="n">formula</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">bw</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">w_vals</span><span class="p">:</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">any</span><span class="p">:</span>
+
+    <span class="c1"># Convert pandas dataframe to R dataframe</span>
+    <span class="k">with</span> <span class="n">localconverter</span><span class="p">(</span><span class="n">robjects</span><span class="o">.</span><span class="n">default_converter</span> <span class="o">+</span> <span class="n">pandas2ri</span><span class="o">.</span><span class="n">converter</span><span class="p">):</span>
+        <span class="n">r_data</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">conversion</span><span class="o">.</span><span class="n">py2rpy</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
+
+    <span class="c1"># Convert numpy array to R vector</span>
+    <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">w_vals</span><span class="p">,</span> <span class="n">robjects</span><span class="o">.</span><span class="n">vectors</span><span class="o">.</span><span class="n">FloatVector</span><span class="p">):</span>
+        <span class="n">w_vals</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">FloatVector</span><span class="p">(</span><span class="n">w_vals</span><span class="p">)</span>
+
+    <span class="c1"># Convert bw to R vector</span>
+    <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">bw</span><span class="p">,</span> <span class="n">robjects</span><span class="o">.</span><span class="n">vectors</span><span class="o">.</span><span class="n">FloatVector</span><span class="p">):</span>
+        <span class="n">bw</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">FloatVector</span><span class="p">([</span><span class="n">bw</span><span class="p">])</span>
+
+    <span class="c1"># collect the function from R</span>
+    <span class="n">r_locpol</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">globalenv</span><span class="p">[</span><span class="s1">&#39;r_locpol&#39;</span><span class="p">]</span>
+
+    <span class="c1"># call the function</span>
+    <span class="n">results</span> <span class="o">=</span> <span class="n">r_locpol</span><span class="p">(</span><span class="n">data</span> <span class="o">=</span> <span class="n">r_data</span><span class="p">,</span>
+                       <span class="n">formula</span> <span class="o">=</span> <span class="n">formula</span><span class="p">,</span> 
+                       <span class="n">bw</span> <span class="o">=</span> <span class="n">bw</span><span class="p">,</span>
+                       <span class="n">w_vals</span> <span class="o">=</span> <span class="n">w_vals</span><span class="p">)</span>
+
+    <span class="c1"># Convert results to python (R vector to numpy array)</span>
+    <span class="k">with</span> <span class="n">localconverter</span><span class="p">(</span><span class="n">robjects</span><span class="o">.</span><span class="n">default_converter</span> <span class="o">+</span> 
+                        <span class="n">robjects</span><span class="o">.</span><span class="n">pandas2ri</span><span class="o">.</span><span class="n">converter</span><span class="p">):</span>
+        <span class="n">py_results</span> <span class="o">=</span> <span class="n">robjects</span><span class="o">.</span><span class="n">conversion</span><span class="o">.</span><span class="n">rpy2py</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
+
+    <span class="k">return</span> <span class="n">py_results</span>
+
+
 
 <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
     <span class="n">x0</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
diff --git a/docs/_sources/CHANGELOG.md.txt b/docs/_sources/CHANGELOG.md.txt
index 0778f92..5340aa9 100644
--- a/docs/_sources/CHANGELOG.md.txt
+++ b/docs/_sources/CHANGELOG.md.txt
@@ -3,16 +3,10 @@
 See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) for more details.
 
 
-## [Unreleased]
-### Added
-
-- Database class to handle database connection and queries.
+## pycafe 0.0.3 - 2022-06-07
 
-### Changed
-- 
-
-### Fixed 
-- 
+### Added
 
-### Removed
-- 
\ No newline at end of file
+- Exposure response function for parametric, semi-parametric and non-parametric models.
+- Estimation of pseudo-population using weighting and matching approaches.
+- Estimation of GPS using normal and kernel approaches. 
diff --git a/docs/_sources/about.rst.txt b/docs/_sources/about.rst.txt
index 0b66bc7..07b6e33 100644
--- a/docs/_sources/about.rst.txt
+++ b/docs/_sources/about.rst.txt
@@ -1,4 +1,6 @@
 About
 =====
-About pycausalgps ... 
+
+
+pycausalgps is a Python library designed for implementing matching on Generalized Propensity Scores (GPS) in scenarios involving continuous exposures. The approach enables estimating causal effects using observational data tailored explicitly for settings with continuous exposures. By introducing a novel framework for GPS caliper matching, we provide a method that simultaneously matches on both the estimated GPS and exposure levels, ensuring comprehensive adjustment for confounding bias. This library offers a reliable and efficient solution for researchers and data scientists seeking to analyze causal relationships in the presence of continuous treatment variables under potential outcome framework.
 
diff --git a/docs/_sources/git_branching.rst.txt b/docs/_sources/git_branching.rst.txt
index 4d3a2e2..f78178f 100644
--- a/docs/_sources/git_branching.rst.txt
+++ b/docs/_sources/git_branching.rst.txt
@@ -1,4 +1,19 @@
 Git Branching Model
 ===================
 
-Bring it in from the R package ...
\ No newline at end of file
+In this package, we primarily adhere to the successful Git branching model proposed by Vincent Driessen in his article, `A successful Git branching model <https://nvie.com/posts/a-successful-git-branching-model/>`_ proposed by Vincent Driessen. Here are the summary of the branches:
+
+- **main**: The main branch hosts released software packages and is accessible only to project maintainers.
+
+- **develop**: The develop branch serves as the main branch, where the source code at HEAD always reflects the latest development changes for the upcoming release.
+
+- **supporting branches**: Several supporting branches exist, with three primary types for which we suggest contributors follow specific naming conventions:
+
+    + **feature**: Feature branches are created to add new features to the software. Names should follow the format "iss[issue_number]_short_description" (e.g., "iss12_add_rotation" for issue #12, involving the addition of time series rotation). Starting with the issue number allows for easy reference to issue details when necessary. While feature branches are temporary, this naming convention helps developers navigate the codebase efficiently.
+
+    + **hotfix**: Hotfix branches are used exclusively to fix bugs in released packages. After a bug fix, the third digit of the version number should increment by one (e.g., 1.4.2 -> 1.4.3). These branches should be prefixed with "hotfix" and followed by the upcoming version number (e.g., "hotfix_1.4.3").
+
+    + **release**: Release branches facilitate the preparation of new production releases. They are typically not used unless the community is working on multiple releases simultaneously (e.g., pre-alpha, alpha, beta, stable, etc.).
+
+By adhering to this Git branching model, we aim to maintain a streamlined and organized codebase, allowing for efficient collaboration and development.
+
diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt
index 557a563..d1c47c0 100644
--- a/docs/_sources/index.rst.txt
+++ b/docs/_sources/index.rst.txt
@@ -41,9 +41,9 @@ pycausalgps is a Python library for implementing matching on generalized propens
 
    setup_env
    quickstart
-   gps
    faq
 
+
 .. toctree::
    :glob:
    :maxdepth: 2
@@ -63,10 +63,6 @@ pycausalgps is a Python library for implementing matching on generalized propens
    CHANGELOG
 
 
-
-
-
-
 Indices and tables
 ==================
 
diff --git a/docs/_sources/quickstart.rst.txt b/docs/_sources/quickstart.rst.txt
index 1512d2d..4b8ed18 100644
--- a/docs/_sources/quickstart.rst.txt
+++ b/docs/_sources/quickstart.rst.txt
@@ -4,27 +4,411 @@ Quickstart
 Installing the Package
 ----------------------
 
-1. Install the package using pip::
+Install the package using pip:
 
-   *TBD*
+.. code-block:: bash
 
-2. Install the package from Github::
+    $ pip install pycausalgps
 
-   *TBD*
+See the `Setting Up Environment <installation.html>`_ for more details.
 
 
-The Whole Game
---------------
+Generating Synthetic Data
+-------------------------
 
-A processing is described under a project. Although users can use classes individually, we recommend using the project controller because it is the only way to generate a flow of processing and keep track of the results in the database. Each processing session requires an instance of project controller. 
+The package provides a function to generate synthetic data. 
 
 .. code-block:: python
 
-    >>> from pycausalgps.project_controller import ProjectController
-    >>> pc = ProjectController(db_path = 'test_database.sqlite')   
+    >>> from pycausalgps.base.utils import generate_syn_pop
+    >>> data = generate_syn_pop(sample_size=1000, 
+                                seed_val=456, 
+                                outcome_sd=0.25, 
+                                gps_spec=1, 
+                                cova_spec=2)
 
 
-The project controller manages the projects in the database. The project controller will connect to the database if the provided database already exists. Otherwise, it will create a new database. We assume that the database did not exist.
 
+Estimating GPS values
+---------------------
+
+Two approaches are provided to estimate GPS values. These approaches are different 
+based on the type of gps_denisty specified. These approaches are `normal` and `kernel`.
+
+Example of `normal` approach:
++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.base import GeneralizedPropensityScore
+
+    params = {"gps_density": "normal",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps = GeneralizedPropensityScore(data, params)
+
+
+Example of `kernel` approach:
++++++++++++++++++++++++++++++
+
+
+.. code-block:: python
+    
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.base import GeneralizedPropensityScore
+
+    params = {"gps_density": "kernel",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps = GeneralizedPropensityScore(data, params)
+
+
+Generating Pseudo-population
+----------------------------
+
+There are two implemented methdos to generate pseudo-population. These methods
+are: `weighting` and `matching`.
+
+Example of `weighting` approach:
+++++++++++++++++++++++++++++++++
+
+
+.. code-block:: python
+
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps_params = {"gps_density": "normal",
+                  "exposure_column": "treat",
+                  "covariate_column_num": ["cf1", 
+                                           "cf2", 
+                                           "cf3", 
+                                           "cf4", 
+                                           "cf6"],
+                  "covariate_column_cat": ["cf5"],
+                  "libs":{
+                          "xgboost":{
+                                 "n_estimators": 100,
+                                 "max_depth": 3,
+                                 "learning_rate": 0.1,
+                                 "test_rate": 0.2,
+                                 "random_state": 42,
+                                 "n_jobs": 12
+                                 }
+                             }
+    }
+    
+    gps = GeneralizedPropensityScore(data, gps_params)
+    
+    results = gps.get_results()
+    gps_data = {
+        'data' : results.get("data"),
+        'gps_density' : results.get("gps_density")
+        'gps_minmax': results.get("gps_minmax")
+    }
+    
+    pspop_params = {"approach" : "weighting", 
+                    "exposure_column": "treat",
+                    "covariate_column_num": ["cf1", 
+                                             "cf2", 
+                                             "cf3", 
+                                             "cf4", 
+                                             "cf6"],
+                    "covariate_column_cat": ["cf5"],}
+    
+    pspop = PseudoPopulation(data=data, 
+                             gps_data=gps_data, 
+                             params=pspop_params)
+
+
+Example of `matching` approach:
++++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+    
+    
+    # matching
+    gps_params = {"gps_density": "normal",
+                  "exposure_column": "treat",
+                  "covariate_column_num": ["cf1", 
+                                           "cf2", 
+                                           "cf3", 
+                                           "cf4", 
+                                           "cf6"],
+                  "covariate_column_cat": ["cf5"],
+                  "libs":{
+                          "xgboost":{
+                                 "n_estimators": 100,
+                                 "max_depth": 3,
+                                 "learning_rate": 0.1,
+                                 "test_rate": 0.2,
+                                 "random_state": 42,
+                                 "n_jobs": 12
+                                 }
+                             }
+    }
+    
+    data = generate_syn_pop(sample_size=500, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+        
+    gps = GeneralizedPropensityScore(data, gps_params)
+    
+    results = gps.get_results()
+    gps_data = {
+        'data' : results.get("data"),
+        'gps_density' : results.get("gps_density"),
+        'gps_minmax': results.get("gps_minmax")
+    }
+    
+    
+    
+    pspop_params = {"approach" : "matching", 
+                    "exposure_column": "treat",
+                    "covariate_column_num": ["cf1", 
+                                             "cf2", 
+                                             "cf3", 
+                                             "cf4", 
+                                             "cf6"],
+                    "covariate_column_cat": ["cf5"],
+                    "control_params": {"caliper": 1.0,
+                                       "scale": 0.5,
+                                       "dist_measure": "l1",
+                                       "bin_seq": None},
+                    "run_params": {"n_thread": 12,
+                                   "chunk_size": 500},
+    }
+    
+    pspop = PseudoPopulation(data=data, 
+                             gps_data=gps_data, 
+                             params=pspop_params)
+
+
+.. note::
+
+   Note that the matching approach is computationally expensive and internally
+   uses multiprocessing. If you encounter any runtime error, try put the code
+   inside a `if __name__ == '__main__':` block.
+
+
+Exposure Response Function
+--------------------------
+
+The package provides a function to estimate exposure response function. There
+are three implemented methods to estimate exposure response function. These
+methods are: `parametric`, `semiparametric` and `nonparametric`.
+
+Example of `parametric` approach:
++++++++++++++++++++++++++++++++++
+
+The parametric approach estimates the hazard ratios using a parametric
+regression model. In the backend we us gnm R package to implement generalized 
+additive models.
+
+.. code-block:: python
+
+    import pandas as pd
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+    from pycausalgps.rscripts.rfunctions import estimate_pmetric_erf
+
+    gps_params = {"gps_density": "normal",
+              "exposure_column": "treat",
+              "covariate_column_num": ["cf1", 
+                                       "cf2", 
+                                       "cf3", 
+                                       "cf4", 
+                                       "cf6"],
+              "covariate_column_cat": ["cf5"],
+              "libs":{
+                      "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+
+    data = generate_syn_pop(sample_size=200, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+        
+    gps = GeneralizedPropensityScore(data, gps_params)
+
+    results = gps.get_results()
+    gps_data = {
+        'data' : results.get("data"),
+        'gps_density' : results.get("gps_density")
+    }
+
+    pspop_params = {"approach" : "weighting", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                         "cf2", 
+                                         "cf3", 
+                                         "cf4", 
+                                         "cf6"],
+                "covariate_column_cat": ["cf5"],}
+
+    ps_pop = PseudoPopulation(data=data, 
+                              gps_data=gps_data, 
+                              params=pspop_params)
+
+    ps_results = ps_pop.get_results()
+    erf_pmetric = estimate_pmetric_erf(formula="Y ~ treat", 
+                                       family="gaussian", 
+                                       data = ps_results.get("data"))
+
+Example of `semiparametric` approach:
++++++++++++++++++++++++++++++++++++++
+
+The semiparametric approach estimates the smoothed exposure-response function
+using generalized additive model with splines. In the backend we us `gam` R 
+package to implement generalized additive models.
+
+
+.. code-block:: python
+
+    # Code from pmetric approach
+    from pycausalgps.rscripts.rfunctions import estimate_semipmetric_erf
+
+    erf_pmetric = estimate_semipmetric_erf(formula="Y ~ treat", 
+                                           family="gaussian", 
+                                           data = ps_results.get("data"))
+
+
+Example of `nonparametric` approach:
+++++++++++++++++++++++++++++++++++++
+
+The nonparametric approach estimates the smoothed exposure-response function
+using kernel smoothing approach. In the backend we us `locpol` R package to 
+implemetn locpol polynomial fitting with a kernel weight. A data-driven 
+bandwidth selection is implemented. 
+
+
+.. code-block:: python
+    
+    import pandas as pd
+    import numpy as np
+    import matplotlib.pyplot as plt
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+    from pycausalgps.erf_helper import estimate_npmetric_erf
+
+    gps_params = {"gps_density": "normal",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                         "n_estimators": 100,
+                         "max_depth": 3,
+                         "learning_rate": 0.1,
+                         "test_rate": 0.2,
+                         "random_state": 42
+                         }
+                     }
+    }
+
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+
+    gps = GeneralizedPropensityScore(data, gps_params)
+
+    results = gps.get_results()
+
+    gps_data = {
+    'data' : results.get("data"),
+    'gps_density' : results.get("gps_density")
+    }
+
+    pspop_params = {"approach" : "weighting", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                        "cf2", 
+                                        "cf3", 
+                                        "cf4", 
+                                        "cf6"],
+                "covariate_column_cat": ["cf5"],}
+
+    ps_pop = PseudoPopulation(data=data, gps_data=gps_data, params=pspop_params)
+    ps_results = ps_pop.get_results()
+
+    m_Y = ps_results.get("data")["Y"].to_numpy()
+    m_w = ps_results.get("data")["treat"].to_numpy()
+    counter_weight = ps_results.get("data")["counter_weight"].to_numpy()
+    bw_seq = np.arange(0.1, 4.0, 0.01)
+    w_vals = np.arange(1, 20, 0.2)
+    nthread = 12
+
+    erf_pmetric = estimate_npmetric_erf(m_Y, m_w, counter_weight, 
+                                    bw_seq, w_vals, nthread)
 
 
diff --git a/docs/_sources/setup_env.rst.txt b/docs/_sources/setup_env.rst.txt
index a488948..5432c96 100644
--- a/docs/_sources/setup_env.rst.txt
+++ b/docs/_sources/setup_env.rst.txt
@@ -20,7 +20,7 @@ lightweight version of anaconda.
 
 .. code-block:: console
 
-    $ conda create --name your_venv python=3.7
+    $ conda create --name your_venv python=3.10
 
 Type 'y' (yes) for popup questions.
 
@@ -30,11 +30,13 @@ Type 'y' (yes) for popup questions.
 
     $ conda activate your_venv
 
-Install **PyCausalGPS** (Step 3 or Step 4)
+Install **pycausalgps** (Step 3 or Step 4)
 
 - Step 3: Directly from PyPI
 
-TBD
+.. code-block:: console
+
+    $ pip3 install pycausalgps
 
 - Step 4: Install from Github package in developement mode
 
@@ -85,8 +87,4 @@ open terminal and fire up notebook:
 
 At the top right corner, there is a button labeled `New` key. Choose your 
 recently created kernel (in this example: your_venv_name). Choosing a kernel 
-will open a new tab that you can work on.
-
-pipenv
-------
-TBD
\ No newline at end of file
+will open a new tab that you can work on.
\ No newline at end of file
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
deleted file mode 100644
index ffd2a01..0000000
--- a/docs/_static/custom.css
+++ /dev/null
@@ -1,6 +0,0 @@
-/* Change the width and height values as needed */
-img[alt="Logo"] {
-    width: 150px;
-    height: 150px;
-}
-
diff --git a/docs/about.html b/docs/about.html
index 6ce0e44..83d6d8c 100644
--- a/docs/about.html
+++ b/docs/about.html
@@ -6,7 +6,6 @@
   <title>About &mdash; pycausalgps 0.0.1 documentation</title>
       <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
       <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
-      <link rel="stylesheet" href="_static/custom.css" type="text/css" />
   <!--[if lt IE 9]>
     <script src="_static/js/html5shiv.min.js"></script>
   <![endif]-->
@@ -48,9 +47,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -115,7 +111,7 @@
 </style>
 <div class="section" id="about">
 <h1>About<a class="headerlink" href="#about" title="Permalink to this headline"></a></h1>
-<p>About pycausalgps …</p>
+<p>pycausalgps is a Python library designed for implementing matching on Generalized Propensity Scores (GPS) in scenarios involving continuous exposures. The approach enables estimating causal effects using observational data tailored explicitly for settings with continuous exposures. By introducing a novel framework for GPS caliper matching, we provide a method that simultaneously matches on both the estimated GPS and exposure levels, ensuring comprehensive adjustment for confounding bias. This library offers a reliable and efficient solution for researchers and data scientists seeking to analyze causal relationships in the presence of continuous treatment variables under potential outcome framework.</p>
 </div>
 
 
diff --git a/docs/contact.html b/docs/contact.html
index 4083e37..c5a4079 100644
--- a/docs/contact.html
+++ b/docs/contact.html
@@ -47,9 +47,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
diff --git a/docs/documentation.html b/docs/documentation.html
index 092be12..a1a417e 100644
--- a/docs/documentation.html
+++ b/docs/documentation.html
@@ -47,9 +47,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul class="current">
diff --git a/docs/faq.html b/docs/faq.html
index e4651d2..3550f88 100644
--- a/docs/faq.html
+++ b/docs/faq.html
@@ -6,7 +6,6 @@
   <title>Frequently Asked Questions &mdash; pycausalgps 0.0.1 documentation</title>
       <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
       <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
-      <link rel="stylesheet" href="_static/custom.css" type="text/css" />
   <!--[if lt IE 9]>
     <script src="_static/js/html5shiv.min.js"></script>
   <![endif]-->
@@ -19,9 +18,7 @@
     <script src="_static/js/theme.js"></script>
     <link rel="author" title="About these documents" href="about.html" />
     <link rel="index" title="Index" href="genindex.html" />
-    <link rel="search" title="Search" href="search.html" />
-    <link rel="next" title="Development Environment" href="setup_env_dev.html" />
-    <link rel="prev" title="Estimating Generalized Propensity Score (GPS)" href="gps.html" /> 
+    <link rel="search" title="Search" href="search.html" /> 
 </head>
 
 <body class="wy-body-for-nav"> 
@@ -45,12 +42,9 @@
 <li class="toctree-l1"><a class="reference internal" href="about.html">About</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Researchers</span></p>
-<ul class="current">
+<ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1 current"><a class="current reference internal" href="#">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -124,10 +118,7 @@ <h1>Frequently Asked Questions<a class="headerlink" href="#frequently-asked-ques
 
            </div>
           </div>
-          <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
-        <a href="gps.html" class="btn btn-neutral float-left" title="Estimating Generalized Propensity Score (GPS)" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
-        <a href="setup_env_dev.html" class="btn btn-neutral float-right" title="Development Environment" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
-    </div>
+          <footer>
 
   <hr/>
 
diff --git a/docs/genindex.html b/docs/genindex.html
index 09b27e5..871bb06 100644
--- a/docs/genindex.html
+++ b/docs/genindex.html
@@ -45,8 +45,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
@@ -89,13 +87,28 @@
 <h1 id="index">Index</h1>
 
 <div class="genindex-jumpbox">
- <a href="#C"><strong>C</strong></a>
+ <a href="#_"><strong>_</strong></a>
+ | <a href="#C"><strong>C</strong></a>
  | <a href="#G"><strong>G</strong></a>
  | <a href="#M"><strong>M</strong></a>
  | <a href="#P"><strong>P</strong></a>
  | <a href="#X"><strong>X</strong></a>
  
 </div>
+<h2 id="_">_</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+  <td style="width: 33%; vertical-align: top;"><ul>
+      <li><a href="modules.html#pycausalgps.gps.GeneralizedPropensityScore._check_data">_check_data() (pycausalgps.gps.GeneralizedPropensityScore method)</a>
+</li>
+  </ul></td>
+  <td style="width: 33%; vertical-align: top;"><ul>
+      <li><a href="modules.html#pycausalgps.gps.GeneralizedPropensityScore._check_params">_check_params() (pycausalgps.gps.GeneralizedPropensityScore method)</a>
+</li>
+      <li><a href="modules.html#pycausalgps.gps.GeneralizedPropensityScore._compute_gps">_compute_gps() (pycausalgps.gps.GeneralizedPropensityScore method)</a>
+</li>
+  </ul></td>
+</tr></table>
+
 <h2 id="C">C</h2>
 <table style="width: 100%" class="indextable genindextable"><tr>
   <td style="width: 33%; vertical-align: top;"><ul>
diff --git a/docs/git_branching.html b/docs/git_branching.html
index 10e07b1..21af735 100644
--- a/docs/git_branching.html
+++ b/docs/git_branching.html
@@ -6,7 +6,6 @@
   <title>Git Branching Model &mdash; pycausalgps 0.0.1 documentation</title>
       <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
       <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
-      <link rel="stylesheet" href="_static/custom.css" type="text/css" />
   <!--[if lt IE 9]>
     <script src="_static/js/html5shiv.min.js"></script>
   <![endif]-->
@@ -48,9 +47,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul class="current">
@@ -115,7 +111,21 @@
 </style>
 <div class="section" id="git-branching-model">
 <h1>Git Branching Model<a class="headerlink" href="#git-branching-model" title="Permalink to this headline"></a></h1>
-<p>Bring it in from the R package …</p>
+<p>In this package, we primarily adhere to the successful Git branching model proposed by Vincent Driessen in his article, <a class="reference external" href="https://nvie.com/posts/a-successful-git-branching-model/">A successful Git branching model</a> proposed by Vincent Driessen. Here are the summary of the branches:</p>
+<ul>
+<li><p><strong>main</strong>: The main branch hosts released software packages and is accessible only to project maintainers.</p></li>
+<li><p><strong>develop</strong>: The develop branch serves as the main branch, where the source code at HEAD always reflects the latest development changes for the upcoming release.</p></li>
+<li><p><strong>supporting branches</strong>: Several supporting branches exist, with three primary types for which we suggest contributors follow specific naming conventions:</p>
+<blockquote>
+<div><ul class="simple">
+<li><p><strong>feature</strong>: Feature branches are created to add new features to the software. Names should follow the format “iss[issue_number]_short_description” (e.g., “iss12_add_rotation” for issue #12, involving the addition of time series rotation). Starting with the issue number allows for easy reference to issue details when necessary. While feature branches are temporary, this naming convention helps developers navigate the codebase efficiently.</p></li>
+<li><p><strong>hotfix</strong>: Hotfix branches are used exclusively to fix bugs in released packages. After a bug fix, the third digit of the version number should increment by one (e.g., 1.4.2 -&gt; 1.4.3). These branches should be prefixed with “hotfix” and followed by the upcoming version number (e.g., “hotfix_1.4.3”).</p></li>
+<li><p><strong>release</strong>: Release branches facilitate the preparation of new production releases. They are typically not used unless the community is working on multiple releases simultaneously (e.g., pre-alpha, alpha, beta, stable, etc.).</p></li>
+</ul>
+</div></blockquote>
+</li>
+</ul>
+<p>By adhering to this Git branching model, we aim to maintain a streamlined and organized codebase, allowing for efficient collaboration and development.</p>
 </div>
 
 
diff --git a/docs/gps.html b/docs/gps.html
index a7ad3aa..8db1e00 100644
--- a/docs/gps.html
+++ b/docs/gps.html
@@ -20,9 +20,7 @@
     <script src="_static/js/theme.js"></script>
     <link rel="author" title="About these documents" href="about.html" />
     <link rel="index" title="Index" href="genindex.html" />
-    <link rel="search" title="Search" href="search.html" />
-    <link rel="next" title="Frequently Asked Questions" href="faq.html" />
-    <link rel="prev" title="Quickstart" href="quickstart.html" /> 
+    <link rel="search" title="Search" href="search.html" /> 
 </head>
 
 <body class="wy-body-for-nav"> 
@@ -46,16 +44,9 @@
 <li class="toctree-l1"><a class="reference internal" href="about.html">About</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Researchers</span></p>
-<ul class="current">
+<ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1 current"><a class="current reference internal" href="#">Estimating Generalized Propensity Score (GPS)</a><ul>
-<li class="toctree-l2"><a class="reference internal" href="#parametric">Parametric</a></li>
-<li class="toctree-l2"><a class="reference internal" href="#non-parametric">Non-parametric</a></li>
-</ul>
-</li>
-<li class="toctree-l1"><a class="reference internal" href="#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -150,10 +141,7 @@ <h1>Computing GPS Values<a class="headerlink" href="#computing-gps-values" title
 
            </div>
           </div>
-          <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
-        <a href="quickstart.html" class="btn btn-neutral float-left" title="Quickstart" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
-        <a href="faq.html" class="btn btn-neutral float-right" title="Frequently Asked Questions" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
-    </div>
+          <footer>
 
   <hr/>
 
diff --git a/docs/index.html b/docs/index.html
index 07a75d2..6ff7177 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -46,8 +46,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
@@ -131,20 +129,16 @@ <h1>pycausalgps<a class="headerlink" href="#pycausalgps" title="Permalink to thi
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="setup_env.html#conda">Conda</a></li>
-<li class="toctree-l2"><a class="reference internal" href="setup_env.html#pipenv">pipenv</a></li>
 </ul>
 </li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="quickstart.html#installing-the-package">Installing the Package</a></li>
-<li class="toctree-l2"><a class="reference internal" href="quickstart.html#the-whole-game">The Whole Game</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#generating-synthetic-data">Generating Synthetic Data</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#estimating-gps-values">Estimating GPS values</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#generating-pseudo-population">Generating Pseudo-population</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#exposure-response-function">Exposure Response Function</a></li>
 </ul>
 </li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a><ul>
-<li class="toctree-l2"><a class="reference internal" href="gps.html#parametric">Parametric</a></li>
-<li class="toctree-l2"><a class="reference internal" href="gps.html#non-parametric">Non-parametric</a></li>
-</ul>
-</li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 </div>
@@ -173,7 +167,7 @@ <h1>pycausalgps<a class="headerlink" href="#pycausalgps" title="Permalink to thi
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="contact.html">Contact Us</a></li>
 <li class="toctree-l1"><a class="reference internal" href="CHANGELOG.html">Changelog</a><ul>
-<li class="toctree-l2"><a class="reference internal" href="CHANGELOG.html#unreleased">[Unreleased]</a></li>
+<li class="toctree-l2"><a class="reference internal" href="CHANGELOG.html#pycafe-0-0-3-2022-06-07">pycafe 0.0.3 - 2022-06-07</a></li>
 </ul>
 </li>
 </ul>
diff --git a/docs/modules.html b/docs/modules.html
index 39f344e..75e58ea 100644
--- a/docs/modules.html
+++ b/docs/modules.html
@@ -47,9 +47,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul class="current">
@@ -132,10 +129,116 @@ <h2>gps.py<a class="headerlink" href="#gps-py" title="Permalink to this headline
 <dt class="field-odd">Parameters</dt>
 <dd class="field-odd"><ul class="simple">
 <li><p><strong>data</strong> (<em>pd.DataFrame</em>) – A pandas DataFrame that contains the data for the GPS computation.</p></li>
-<li><p><strong>params</strong> (<em>dict</em>) – A dictionary of gps parameters.</p></li>
+<li><p><strong>params</strong> (<em>dict</em>) – <p>A dictionary of gps parameters. This includes the folowing required
+parameters:</p>
+<blockquote>
+<div><ul>
+<li><dl class="simple">
+<dt>gps_density: str</dt><dd><p>A string that indicates the density estimation method to be
+used. Available options are “normal” and “kernel”.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>exposure_column: str</dt><dd><p>A string that indicates the name of the exposure column.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>covariate_column_num: list</dt><dd><p>A list of strings that indicates the names of the numerical
+covariate columns.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>covariate_column_cat: list</dt><dd><p>A list of strings that indicates the names of the categorical
+covariate columns.</p>
+</dd>
+</dl>
+</li>
+<li><dl>
+<dt>libs: dict</dt><dd><p>A dictionary of libraries and their parameters. Currently only
+xgboost is supported. Any parameters for hypertuning the
+xgboost model can be passed including:</p>
+<blockquote>
+<div><ul class="simple">
+<li><dl class="simple">
+<dt>n_estimators: int</dt><dd><p>Number of trees to fit.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>learning_rate: float</dt><dd><p>Learning rate for the xgboost model.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>max_depth: int</dt><dd><p>Maximum depth of a tree.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>test_rate: float</dt><dd><p>The proportion of the data to be used for testing.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>random_state: int</dt><dd><p>Random state for the xgboost model.</p>
+</dd>
+</dl>
+</li>
+</ul>
+</div></blockquote>
+</dd>
+</dl>
+</li>
+</ul>
+</div></blockquote>
+</p></li>
+</ul>
+</dd>
+<dt class="field-even">Returns</dt>
+<dd class="field-even"><p><ul class="simple">
+<li><dl class="simple">
+<dt>data: pd.DataFrame</dt><dd><p>The gps and auxilary columns.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>gps_minmax: list</dt><dd><p>A list of the minimum and maximum gps values.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="simple">
+<dt>training_report: dict</dt><dd><p>A dictionary of the training report for the xgboost model.</p>
+</dd>
+</dl>
+</li>
 </ul>
+</p>
+</dd>
+<dt class="field-odd">Return type</dt>
+<dd class="field-odd"><p>GeneralizedPropensityScore object that includes the following attributes</p>
 </dd>
 </dl>
+<dl class="py method">
+<dt class="sig sig-object py" id="pycausalgps.gps.GeneralizedPropensityScore._check_data">
+<span class="sig-name descname"><span class="pre">_check_data</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/pycausalgps/gps.html#GeneralizedPropensityScore._check_data"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#pycausalgps.gps.GeneralizedPropensityScore._check_data" title="Permalink to this definition"></a></dt>
+<dd><p>Check if the data is in the correct format.</p>
+</dd></dl>
+
+<dl class="py method">
+<dt class="sig sig-object py" id="pycausalgps.gps.GeneralizedPropensityScore._check_params">
+<span class="sig-name descname"><span class="pre">_check_params</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/pycausalgps/gps.html#GeneralizedPropensityScore._check_params"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#pycausalgps.gps.GeneralizedPropensityScore._check_params" title="Permalink to this definition"></a></dt>
+<dd><p>Check if the params are in the correct format.</p>
+</dd></dl>
+
+<dl class="py method">
+<dt class="sig sig-object py" id="pycausalgps.gps.GeneralizedPropensityScore._compute_gps">
+<span class="sig-name descname"><span class="pre">_compute_gps</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="reference internal" href="_modules/pycausalgps/gps.html#GeneralizedPropensityScore._compute_gps"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#pycausalgps.gps.GeneralizedPropensityScore._compute_gps" title="Permalink to this definition"></a></dt>
+<dd><p>Compute GPS based on the provided parameters.</p>
+</dd></dl>
+
 <dl class="py method">
 <dt class="sig sig-object py" id="pycausalgps.gps.GeneralizedPropensityScore.compute_gps">
 <span class="sig-name descname"><span class="pre">compute_gps</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="reference internal" href="_modules/pycausalgps/gps.html#GeneralizedPropensityScore.compute_gps"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#pycausalgps.gps.GeneralizedPropensityScore.compute_gps" title="Permalink to this definition"></a></dt>
diff --git a/docs/objects.inv b/docs/objects.inv
index ea8cd9a..b29733b 100644
Binary files a/docs/objects.inv and b/docs/objects.inv differ
diff --git a/docs/py-modindex.html b/docs/py-modindex.html
index 9813d0a..3909f08 100644
--- a/docs/py-modindex.html
+++ b/docs/py-modindex.html
@@ -48,8 +48,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
diff --git a/docs/quickstart.html b/docs/quickstart.html
index 80f9352..a1defe5 100644
--- a/docs/quickstart.html
+++ b/docs/quickstart.html
@@ -19,7 +19,7 @@
     <link rel="author" title="About these documents" href="about.html" />
     <link rel="index" title="Index" href="genindex.html" />
     <link rel="search" title="Search" href="search.html" />
-    <link rel="next" title="Estimating Generalized Propensity Score (GPS)" href="gps.html" />
+    <link rel="next" title="Development Environment" href="setup_env_dev.html" />
     <link rel="prev" title="Setting Up Environment" href="setup_env.html" /> 
 </head>
 
@@ -48,12 +48,25 @@
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1 current"><a class="current reference internal" href="#">Quickstart</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="#installing-the-package">Installing the Package</a></li>
-<li class="toctree-l2"><a class="reference internal" href="#the-whole-game">The Whole Game</a></li>
+<li class="toctree-l2"><a class="reference internal" href="#generating-synthetic-data">Generating Synthetic Data</a></li>
+<li class="toctree-l2"><a class="reference internal" href="#estimating-gps-values">Estimating GPS values</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-normal-approach">Example of <cite>normal</cite> approach:</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-kernel-approach">Example of <cite>kernel</cite> approach:</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="#generating-pseudo-population">Generating Pseudo-population</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-weighting-approach">Example of <cite>weighting</cite> approach:</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-matching-approach">Example of <cite>matching</cite> approach:</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="#exposure-response-function">Exposure Response Function</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-parametric-approach">Example of <cite>parametric</cite> approach:</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-semiparametric-approach">Example of <cite>semiparametric</cite> approach:</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#example-of-nonparametric-approach">Example of <cite>nonparametric</cite> approach:</a></li>
+</ul>
+</li>
 </ul>
 </li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -120,27 +133,382 @@
 <h1>Quickstart<a class="headerlink" href="#quickstart" title="Permalink to this headline"></a></h1>
 <div class="section" id="installing-the-package">
 <h2>Installing the Package<a class="headerlink" href="#installing-the-package" title="Permalink to this headline"></a></h2>
-<ol class="arabic">
-<li><p>Install the package using pip:</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">*</span><span class="n">TBD</span><span class="o">*</span>
+<p>Install the package using pip:</p>
+<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ pip install pycausalgps
 </pre></div>
 </div>
-</li>
-<li><p>Install the package from Github:</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">*</span><span class="n">TBD</span><span class="o">*</span>
+<p>See the <a class="reference external" href="installation.html">Setting Up Environment</a> for more details.</p>
+</div>
+<div class="section" id="generating-synthetic-data">
+<h2>Generating Synthetic Data<a class="headerlink" href="#generating-synthetic-data" title="Permalink to this headline"></a></h2>
+<p>The package provides a function to generate synthetic data.</p>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">pycausalgps.base.utils</span> <span class="kn">import</span> <span class="n">generate_syn_pop</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
+<span class="go">                            seed_val=456,</span>
+<span class="go">                            outcome_sd=0.25,</span>
+<span class="go">                            gps_spec=1,</span>
+<span class="go">                            cova_spec=2)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="estimating-gps-values">
+<h2>Estimating GPS values<a class="headerlink" href="#estimating-gps-values" title="Permalink to this headline"></a></h2>
+<p>Two approaches are provided to estimate GPS values. These approaches are different
+based on the type of gps_denisty specified. These approaches are <cite>normal</cite> and <cite>kernel</cite>.</p>
+<div class="section" id="example-of-normal-approach">
+<h3>Example of <cite>normal</cite> approach:<a class="headerlink" href="#example-of-normal-approach" title="Permalink to this headline"></a></h3>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pycausalgps.base.utils</span> <span class="kn">import</span> <span class="n">generate_syn_pop</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.base</span> <span class="kn">import</span> <span class="n">GeneralizedPropensityScore</span>
+
+<span class="n">params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="s2">&quot;normal&quot;</span><span class="p">,</span>
+      <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+      <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+      <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+      <span class="s2">&quot;libs&quot;</span><span class="p">:{</span>
+              <span class="s2">&quot;xgboost&quot;</span><span class="p">:{</span>
+                         <span class="s2">&quot;n_estimators&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
+                         <span class="s2">&quot;max_depth&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+                         <span class="s2">&quot;learning_rate&quot;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
+                         <span class="s2">&quot;test_rate&quot;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
+                         <span class="s2">&quot;random_state&quot;</span><span class="p">:</span> <span class="mi">42</span>
+                         <span class="p">}</span>
+                     <span class="p">}</span>
+<span class="p">}</span>
+<span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
+                        <span class="n">seed_val</span><span class="o">=</span><span class="mi">456</span><span class="p">,</span>
+                        <span class="n">outcome_sd</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span>
+                        <span class="n">gps_spec</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
+                        <span class="n">cova_spec</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
+
+<span class="n">gps</span> <span class="o">=</span> <span class="n">GeneralizedPropensityScore</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="example-of-kernel-approach">
+<h3>Example of <cite>kernel</cite> approach:<a class="headerlink" href="#example-of-kernel-approach" title="Permalink to this headline"></a></h3>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pycausalgps.base.utils</span> <span class="kn">import</span> <span class="n">generate_syn_pop</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.base</span> <span class="kn">import</span> <span class="n">GeneralizedPropensityScore</span>
+
+<span class="n">params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="s2">&quot;kernel&quot;</span><span class="p">,</span>
+      <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+      <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+      <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+      <span class="s2">&quot;libs&quot;</span><span class="p">:{</span>
+              <span class="s2">&quot;xgboost&quot;</span><span class="p">:{</span>
+                         <span class="s2">&quot;n_estimators&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
+                         <span class="s2">&quot;max_depth&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+                         <span class="s2">&quot;learning_rate&quot;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
+                         <span class="s2">&quot;test_rate&quot;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
+                         <span class="s2">&quot;random_state&quot;</span><span class="p">:</span> <span class="mi">42</span>
+                         <span class="p">}</span>
+                     <span class="p">}</span>
+<span class="p">}</span>
+<span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
+                        <span class="n">seed_val</span><span class="o">=</span><span class="mi">456</span><span class="p">,</span>
+                        <span class="n">outcome_sd</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span>
+                        <span class="n">gps_spec</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
+                        <span class="n">cova_spec</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
+
+<span class="n">gps</span> <span class="o">=</span> <span class="n">GeneralizedPropensityScore</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+<div class="section" id="generating-pseudo-population">
+<h2>Generating Pseudo-population<a class="headerlink" href="#generating-pseudo-population" title="Permalink to this headline"></a></h2>
+<p>There are two implemented methdos to generate pseudo-population. These methods
+are: <cite>weighting</cite> and <cite>matching</cite>.</p>
+<div class="section" id="example-of-weighting-approach">
+<h3>Example of <cite>weighting</cite> approach:<a class="headerlink" href="#example-of-weighting-approach" title="Permalink to this headline"></a></h3>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
+                        <span class="n">seed_val</span><span class="o">=</span><span class="mi">456</span><span class="p">,</span>
+                        <span class="n">outcome_sd</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span>
+                        <span class="n">gps_spec</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
+                        <span class="n">cova_spec</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
+
+<span class="n">gps_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="s2">&quot;normal&quot;</span><span class="p">,</span>
+              <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+              <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+              <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+              <span class="s2">&quot;libs&quot;</span><span class="p">:{</span>
+                      <span class="s2">&quot;xgboost&quot;</span><span class="p">:{</span>
+                             <span class="s2">&quot;n_estimators&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
+                             <span class="s2">&quot;max_depth&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+                             <span class="s2">&quot;learning_rate&quot;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
+                             <span class="s2">&quot;test_rate&quot;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
+                             <span class="s2">&quot;random_state&quot;</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span>
+                             <span class="s2">&quot;n_jobs&quot;</span><span class="p">:</span> <span class="mi">12</span>
+                             <span class="p">}</span>
+                         <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">gps</span> <span class="o">=</span> <span class="n">GeneralizedPropensityScore</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">gps_params</span><span class="p">)</span>
+
+<span class="n">results</span> <span class="o">=</span> <span class="n">gps</span><span class="o">.</span><span class="n">get_results</span><span class="p">()</span>
+<span class="n">gps_data</span> <span class="o">=</span> <span class="p">{</span>
+    <span class="s1">&#39;data&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">),</span>
+    <span class="s1">&#39;gps_density&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_density&quot;</span><span class="p">)</span>
+    <span class="s1">&#39;gps_minmax&#39;</span><span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_minmax&quot;</span><span class="p">)</span>
+<span class="p">}</span>
+
+<span class="n">pspop_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;approach&quot;</span> <span class="p">:</span> <span class="s2">&quot;weighting&quot;</span><span class="p">,</span>
+                <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+                <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+                <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],}</span>
+
+<span class="n">pspop</span> <span class="o">=</span> <span class="n">PseudoPopulation</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span>
+                         <span class="n">gps_data</span><span class="o">=</span><span class="n">gps_data</span><span class="p">,</span>
+                         <span class="n">params</span><span class="o">=</span><span class="n">pspop_params</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="example-of-matching-approach">
+<h3>Example of <cite>matching</cite> approach:<a class="headerlink" href="#example-of-matching-approach" title="Permalink to this headline"></a></h3>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pycausalgps.base.utils</span> <span class="kn">import</span> <span class="n">generate_syn_pop</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.gps</span> <span class="kn">import</span> <span class="n">GeneralizedPropensityScore</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.pseudo_population</span> <span class="kn">import</span> <span class="n">PseudoPopulation</span>
+
+
+<span class="c1"># matching</span>
+<span class="n">gps_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="s2">&quot;normal&quot;</span><span class="p">,</span>
+              <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+              <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                       <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+              <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+              <span class="s2">&quot;libs&quot;</span><span class="p">:{</span>
+                      <span class="s2">&quot;xgboost&quot;</span><span class="p">:{</span>
+                             <span class="s2">&quot;n_estimators&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
+                             <span class="s2">&quot;max_depth&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+                             <span class="s2">&quot;learning_rate&quot;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
+                             <span class="s2">&quot;test_rate&quot;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
+                             <span class="s2">&quot;random_state&quot;</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span>
+                             <span class="s2">&quot;n_jobs&quot;</span><span class="p">:</span> <span class="mi">12</span>
+                             <span class="p">}</span>
+                         <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
+                        <span class="n">seed_val</span><span class="o">=</span><span class="mi">456</span><span class="p">,</span>
+                        <span class="n">outcome_sd</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span>
+                        <span class="n">gps_spec</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
+                        <span class="n">cova_spec</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
+
+<span class="n">gps</span> <span class="o">=</span> <span class="n">GeneralizedPropensityScore</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">gps_params</span><span class="p">)</span>
+
+<span class="n">results</span> <span class="o">=</span> <span class="n">gps</span><span class="o">.</span><span class="n">get_results</span><span class="p">()</span>
+<span class="n">gps_data</span> <span class="o">=</span> <span class="p">{</span>
+    <span class="s1">&#39;data&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">),</span>
+    <span class="s1">&#39;gps_density&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_density&quot;</span><span class="p">),</span>
+    <span class="s1">&#39;gps_minmax&#39;</span><span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_minmax&quot;</span><span class="p">)</span>
+<span class="p">}</span>
+
+
+
+<span class="n">pspop_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;approach&quot;</span> <span class="p">:</span> <span class="s2">&quot;matching&quot;</span><span class="p">,</span>
+                <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+                <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                         <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+                <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+                <span class="s2">&quot;control_params&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;caliper&quot;</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span>
+                                   <span class="s2">&quot;scale&quot;</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">,</span>
+                                   <span class="s2">&quot;dist_measure&quot;</span><span class="p">:</span> <span class="s2">&quot;l1&quot;</span><span class="p">,</span>
+                                   <span class="s2">&quot;bin_seq&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">},</span>
+                <span class="s2">&quot;run_params&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;n_thread&quot;</span><span class="p">:</span> <span class="mi">12</span><span class="p">,</span>
+                               <span class="s2">&quot;chunk_size&quot;</span><span class="p">:</span> <span class="mi">500</span><span class="p">},</span>
+<span class="p">}</span>
+
+<span class="n">pspop</span> <span class="o">=</span> <span class="n">PseudoPopulation</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span>
+                         <span class="n">gps_data</span><span class="o">=</span><span class="n">gps_data</span><span class="p">,</span>
+                         <span class="n">params</span><span class="o">=</span><span class="n">pspop_params</span><span class="p">)</span>
+</pre></div>
+</div>
+<div class="admonition note">
+<p class="admonition-title">Note</p>
+<p>Note that the matching approach is computationally expensive and internally
+uses multiprocessing. If you encounter any runtime error, try put the code
+inside a <cite>if __name__ == ‘__main__’:</cite> block.</p>
+</div>
+</div>
+</div>
+<div class="section" id="exposure-response-function">
+<h2>Exposure Response Function<a class="headerlink" href="#exposure-response-function" title="Permalink to this headline"></a></h2>
+<p>The package provides a function to estimate exposure response function. There
+are three implemented methods to estimate exposure response function. These
+methods are: <cite>parametric</cite>, <cite>semiparametric</cite> and <cite>nonparametric</cite>.</p>
+<div class="section" id="example-of-parametric-approach">
+<h3>Example of <cite>parametric</cite> approach:<a class="headerlink" href="#example-of-parametric-approach" title="Permalink to this headline"></a></h3>
+<p>The parametric approach estimates the hazard ratios using a parametric
+regression model. In the backend we us gnm R package to implement generalized
+additive models.</p>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
+
+<span class="kn">from</span> <span class="nn">pycausalgps.base.utils</span> <span class="kn">import</span> <span class="n">generate_syn_pop</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.gps</span> <span class="kn">import</span> <span class="n">GeneralizedPropensityScore</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.pseudo_population</span> <span class="kn">import</span> <span class="n">PseudoPopulation</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.rscripts.rfunctions</span> <span class="kn">import</span> <span class="n">estimate_pmetric_erf</span>
+
+<span class="n">gps_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="s2">&quot;normal&quot;</span><span class="p">,</span>
+          <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+          <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                   <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                   <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                   <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                   <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+          <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+          <span class="s2">&quot;libs&quot;</span><span class="p">:{</span>
+                  <span class="s2">&quot;xgboost&quot;</span><span class="p">:{</span>
+                         <span class="s2">&quot;n_estimators&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
+                         <span class="s2">&quot;max_depth&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+                         <span class="s2">&quot;learning_rate&quot;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
+                         <span class="s2">&quot;test_rate&quot;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
+                         <span class="s2">&quot;random_state&quot;</span><span class="p">:</span> <span class="mi">42</span>
+                         <span class="p">}</span>
+                     <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span>
+                        <span class="n">seed_val</span><span class="o">=</span><span class="mi">456</span><span class="p">,</span>
+                        <span class="n">outcome_sd</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span>
+                        <span class="n">gps_spec</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
+                        <span class="n">cova_spec</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
+
+<span class="n">gps</span> <span class="o">=</span> <span class="n">GeneralizedPropensityScore</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">gps_params</span><span class="p">)</span>
+
+<span class="n">results</span> <span class="o">=</span> <span class="n">gps</span><span class="o">.</span><span class="n">get_results</span><span class="p">()</span>
+<span class="n">gps_data</span> <span class="o">=</span> <span class="p">{</span>
+    <span class="s1">&#39;data&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">),</span>
+    <span class="s1">&#39;gps_density&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_density&quot;</span><span class="p">)</span>
+<span class="p">}</span>
+
+<span class="n">pspop_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;approach&quot;</span> <span class="p">:</span> <span class="s2">&quot;weighting&quot;</span><span class="p">,</span>
+            <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+            <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                     <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                     <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                     <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                     <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+            <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],}</span>
+
+<span class="n">ps_pop</span> <span class="o">=</span> <span class="n">PseudoPopulation</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span>
+                          <span class="n">gps_data</span><span class="o">=</span><span class="n">gps_data</span><span class="p">,</span>
+                          <span class="n">params</span><span class="o">=</span><span class="n">pspop_params</span><span class="p">)</span>
+
+<span class="n">ps_results</span> <span class="o">=</span> <span class="n">ps_pop</span><span class="o">.</span><span class="n">get_results</span><span class="p">()</span>
+<span class="n">erf_pmetric</span> <span class="o">=</span> <span class="n">estimate_pmetric_erf</span><span class="p">(</span><span class="n">formula</span><span class="o">=</span><span class="s2">&quot;Y ~ treat&quot;</span><span class="p">,</span>
+                                   <span class="n">family</span><span class="o">=</span><span class="s2">&quot;gaussian&quot;</span><span class="p">,</span>
+                                   <span class="n">data</span> <span class="o">=</span> <span class="n">ps_results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">))</span>
 </pre></div>
 </div>
-</li>
-</ol>
 </div>
-<div class="section" id="the-whole-game">
-<h2>The Whole Game<a class="headerlink" href="#the-whole-game" title="Permalink to this headline"></a></h2>
-<p>A processing is described under a project. Although users can use classes individually, we recommend using the project controller because it is the only way to generate a flow of processing and keep track of the results in the database. Each processing session requires an instance of project controller.</p>
-<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">pycausalgps.project_controller</span> <span class="kn">import</span> <span class="n">ProjectController</span>
-<span class="gp">&gt;&gt;&gt; </span><span class="n">pc</span> <span class="o">=</span> <span class="n">ProjectController</span><span class="p">(</span><span class="n">db_path</span> <span class="o">=</span> <span class="s1">&#39;test_database.sqlite&#39;</span><span class="p">)</span>
+<div class="section" id="example-of-semiparametric-approach">
+<h3>Example of <cite>semiparametric</cite> approach:<a class="headerlink" href="#example-of-semiparametric-approach" title="Permalink to this headline"></a></h3>
+<p>The semiparametric approach estimates the smoothed exposure-response function
+using generalized additive model with splines. In the backend we us <cite>gam</cite> R
+package to implement generalized additive models.</p>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># Code from pmetric approach</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.rscripts.rfunctions</span> <span class="kn">import</span> <span class="n">estimate_semipmetric_erf</span>
+
+<span class="n">erf_pmetric</span> <span class="o">=</span> <span class="n">estimate_semipmetric_erf</span><span class="p">(</span><span class="n">formula</span><span class="o">=</span><span class="s2">&quot;Y ~ treat&quot;</span><span class="p">,</span>
+                                       <span class="n">family</span><span class="o">=</span><span class="s2">&quot;gaussian&quot;</span><span class="p">,</span>
+                                       <span class="n">data</span> <span class="o">=</span> <span class="n">ps_results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">))</span>
 </pre></div>
 </div>
-<p>The project controller manages the projects in the database. The project controller will connect to the database if the provided database already exists. Otherwise, it will create a new database. We assume that the database did not exist.</p>
+</div>
+<div class="section" id="example-of-nonparametric-approach">
+<h3>Example of <cite>nonparametric</cite> approach:<a class="headerlink" href="#example-of-nonparametric-approach" title="Permalink to this headline"></a></h3>
+<p>The nonparametric approach estimates the smoothed exposure-response function
+using kernel smoothing approach. In the backend we us <cite>locpol</cite> R package to
+implemetn locpol polynomial fitting with a kernel weight. A data-driven
+bandwidth selection is implemented.</p>
+<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
+<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
+<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
+
+<span class="kn">from</span> <span class="nn">pycausalgps.base.utils</span> <span class="kn">import</span> <span class="n">generate_syn_pop</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.gps</span> <span class="kn">import</span> <span class="n">GeneralizedPropensityScore</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.pseudo_population</span> <span class="kn">import</span> <span class="n">PseudoPopulation</span>
+<span class="kn">from</span> <span class="nn">pycausalgps.erf_helper</span> <span class="kn">import</span> <span class="n">estimate_npmetric_erf</span>
+
+<span class="n">gps_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;gps_density&quot;</span><span class="p">:</span> <span class="s2">&quot;normal&quot;</span><span class="p">,</span>
+      <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+      <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                               <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+      <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],</span>
+      <span class="s2">&quot;libs&quot;</span><span class="p">:{</span>
+              <span class="s2">&quot;xgboost&quot;</span><span class="p">:{</span>
+                     <span class="s2">&quot;n_estimators&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
+                     <span class="s2">&quot;max_depth&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
+                     <span class="s2">&quot;learning_rate&quot;</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
+                     <span class="s2">&quot;test_rate&quot;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
+                     <span class="s2">&quot;random_state&quot;</span><span class="p">:</span> <span class="mi">42</span>
+                     <span class="p">}</span>
+                 <span class="p">}</span>
+<span class="p">}</span>
+
+<span class="n">data</span> <span class="o">=</span> <span class="n">generate_syn_pop</span><span class="p">(</span><span class="n">sample_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
+                        <span class="n">seed_val</span><span class="o">=</span><span class="mi">456</span><span class="p">,</span>
+                        <span class="n">outcome_sd</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span>
+                        <span class="n">gps_spec</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
+                        <span class="n">cova_spec</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
+
+<span class="n">gps</span> <span class="o">=</span> <span class="n">GeneralizedPropensityScore</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">gps_params</span><span class="p">)</span>
+
+<span class="n">results</span> <span class="o">=</span> <span class="n">gps</span><span class="o">.</span><span class="n">get_results</span><span class="p">()</span>
+
+<span class="n">gps_data</span> <span class="o">=</span> <span class="p">{</span>
+<span class="s1">&#39;data&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">),</span>
+<span class="s1">&#39;gps_density&#39;</span> <span class="p">:</span> <span class="n">results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;gps_density&quot;</span><span class="p">)</span>
+<span class="p">}</span>
+
+<span class="n">pspop_params</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;approach&quot;</span> <span class="p">:</span> <span class="s2">&quot;weighting&quot;</span><span class="p">,</span>
+            <span class="s2">&quot;exposure_column&quot;</span><span class="p">:</span> <span class="s2">&quot;treat&quot;</span><span class="p">,</span>
+            <span class="s2">&quot;covariate_column_num&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf1&quot;</span><span class="p">,</span>
+                                    <span class="s2">&quot;cf2&quot;</span><span class="p">,</span>
+                                    <span class="s2">&quot;cf3&quot;</span><span class="p">,</span>
+                                    <span class="s2">&quot;cf4&quot;</span><span class="p">,</span>
+                                    <span class="s2">&quot;cf6&quot;</span><span class="p">],</span>
+            <span class="s2">&quot;covariate_column_cat&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;cf5&quot;</span><span class="p">],}</span>
+
+<span class="n">ps_pop</span> <span class="o">=</span> <span class="n">PseudoPopulation</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">gps_data</span><span class="o">=</span><span class="n">gps_data</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="n">pspop_params</span><span class="p">)</span>
+<span class="n">ps_results</span> <span class="o">=</span> <span class="n">ps_pop</span><span class="o">.</span><span class="n">get_results</span><span class="p">()</span>
+
+<span class="n">m_Y</span> <span class="o">=</span> <span class="n">ps_results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">)[</span><span class="s2">&quot;Y&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">to_numpy</span><span class="p">()</span>
+<span class="n">m_w</span> <span class="o">=</span> <span class="n">ps_results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">)[</span><span class="s2">&quot;treat&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">to_numpy</span><span class="p">()</span>
+<span class="n">counter_weight</span> <span class="o">=</span> <span class="n">ps_results</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;data&quot;</span><span class="p">)[</span><span class="s2">&quot;counter_weight&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">to_numpy</span><span class="p">()</span>
+<span class="n">bw_seq</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">4.0</span><span class="p">,</span> <span class="mf">0.01</span><span class="p">)</span>
+<span class="n">w_vals</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">)</span>
+<span class="n">nthread</span> <span class="o">=</span> <span class="mi">12</span>
+
+<span class="n">erf_pmetric</span> <span class="o">=</span> <span class="n">estimate_npmetric_erf</span><span class="p">(</span><span class="n">m_Y</span><span class="p">,</span> <span class="n">m_w</span><span class="p">,</span> <span class="n">counter_weight</span><span class="p">,</span>
+                                <span class="n">bw_seq</span><span class="p">,</span> <span class="n">w_vals</span><span class="p">,</span> <span class="n">nthread</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
 </div>
 </div>
 
@@ -149,7 +517,7 @@ <h2>The Whole Game<a class="headerlink" href="#the-whole-game" title="Permalink
           </div>
           <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
         <a href="setup_env.html" class="btn btn-neutral float-left" title="Setting Up Environment" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
-        <a href="gps.html" class="btn btn-neutral float-right" title="Estimating Generalized Propensity Score (GPS)" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
+        <a href="setup_env_dev.html" class="btn btn-neutral float-right" title="Development Environment" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
     </div>
 
   <hr/>
diff --git a/docs/search.html b/docs/search.html
index 3cbd8c2..9750f50 100644
--- a/docs/search.html
+++ b/docs/search.html
@@ -48,8 +48,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
 <li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
diff --git a/docs/searchindex.js b/docs/searchindex.js
index a4b09d5..853891b 100644
--- a/docs/searchindex.js
+++ b/docs/searchindex.js
@@ -1,1 +1,1 @@
-Search.setIndex({docnames:["CHANGELOG","about","contact","documentation","faq","git_branching","gps","index","modules","quickstart","setup_env","setup_env_dev"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,nbsphinx:4,sphinx:56},filenames:["CHANGELOG.md","about.rst","contact.rst","documentation.rst","faq.rst","git_branching.rst","gps.rst","index.rst","modules.rst","quickstart.rst","setup_env.rst","setup_env_dev.rst"],objects:{"pycausalgps.gps":[[8,1,1,"","GeneralizedPropensityScore"]],"pycausalgps.gps.GeneralizedPropensityScore":[[8,2,1,"","compute_gps"],[8,2,1,"","compute_gps_xgboost"],[8,2,1,"","get_results"],[8,2,1,"","xgb_train_it"]],"pycausalgps.rscripts":[[8,0,0,"-","rfunctions"]],"pycausalgps.rscripts.rfunctions":[[8,3,1,"","compute_absolute_weighted_corr"],[8,3,1,"","compute_density"]],pycausalgps:[[8,0,0,"-","gps"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:function"},terms:{"1":10,"1983":6,"2":10,"2004":6,"3":[3,10,11],"4":10,"5":10,"6":10,"7":[10,11],"class":[0,6,8,9],"do":11,"export":11,"function":8,"import":[3,9,10],"new":[2,7,9,10],"return":8,"static":8,"while":11,A:[4,6,8,9],As:11,At:10,If:11,In:10,That:10,The:[3,6,7,8,10,11],There:[3,6],These:3,_:3,abl:10,about:7,absolut:8,accord:10,action:3,activ:[10,11],ad:[3,10,11],add:3,address:3,adjust:7,all:[3,8],alreadi:9,also:[10,11],although:9,an:[2,3,7,9],anaconda:[10,11],ani:[3,10,11],answer:4,approach:7,ar:[3,6,8,10],arbitrari:10,arrai:8,ask:7,assign:6,assum:9,base:8,becaus:9,bia:7,bodi:3,both:7,branch:7,braun:2,bring:5,button:10,c:10,c_cat:8,c_num:8,calip:7,call:6,can:[6,9,10,11],categor:8,causal:7,chane:11,chang:3,changelog:[3,7],choos:10,code:[3,10,11],command:[3,10,11],complet:11,comput:[7,8],compute_absolute_weighted_corr:8,compute_dens:8,compute_gp:8,compute_gps_xgboost:8,computegp:6,conda:[7,11],condit:6,confound:[7,8],connect:[0,9],contact:7,contain:8,continu:[7,8],contribut:3,control:9,core:8,corner:10,correl:8,covari:[6,8],creat:[2,8,9,10,11],current:11,daniel:2,data:[7,8,10],databas:[0,9],datafram:8,db_path:9,defin:8,densiti:8,depend:11,describ:9,detail:0,develop:[2,3,10],dict:8,dictionari:8,did:9,directli:10,directori:10,discuss:2,docsourc:3,docstr:3,document:[7,11],download:[10,11],dure:3,e:[10,11],each:[3,9],easier:3,edu:2,effect:7,email:2,end:3,env:11,environ:7,environment_dev:11,equat:6,estim:[7,8],evalu:8,exampl:[6,10],exist:[7,9],exposur:[7,8],extend:6,extra:11,featur:11,file:[3,11],fire:10,fix:3,flag:11,flexibl:6,flow:9,folder:[3,10,11],follow:[6,10,11],format:3,frame:8,framework:7,frequent:7,from:[5,9,10,11],fulli:7,g:2,game:7,gener:[2,3,7,8,9],generalizedpropensityscor:8,get_result:8,git:7,github:[2,9,10,11],given:6,gp:7,gps_model:6,handl:0,harvard:2,have:[7,10,11],here:10,higher:11,highli:11,hirano:6,howev:[3,10],imben:6,implement:[6,7],includ:[3,8],index:7,individu:9,infer:7,innov:7,input:8,insid:10,instal:[7,10],instanc:9,introduc:7,ipykernel:[10,11],issu:2,jointli:7,jupyt:[10,11],keep:[0,9],kei:10,kernel:[7,8,10],kernelspec:11,kernl:11,khoshnevi:2,lab:10,label:10,last:10,latest:11,learn:6,level:7,librari:[7,8],lightweight:[10,11],linear:6,link:3,list:[3,11],m:[10,11],machin:6,main:[3,8],mainten:3,make:[10,11],manag:[9,10],manual:11,match:7,mathemat:6,meant:3,method:3,miniconda:[10,11],mode:[10,11],model:[6,7,8],modif:3,modul:[3,7],more:[0,10],n:11,naeem:2,name:[10,11],navig:[10,11],ndarrai:8,need:[3,11],next:10,nkhoshnevi:2,non:7,none:8,note:3,notebook:[10,11],np:8,nsaphx:11,numer:8,numpi:8,object:8,observ:[6,7],one:10,onli:9,open:10,other:10,otherwis:9,packag:[5,7,8,10,11],page:[2,7],panda:8,param:8,paramet:8,parametr:7,part:3,particular:6,pc:9,pd:8,pip3:[10,11],pip:9,pipenv:7,place:3,plan:11,pleas:[2,3,10,11],popup:10,predict:8,predict_al:8,prefix:11,primari:2,privat:3,probabl:6,process:[9,10],project:[2,8,9,10],project_control:9,projectcontrol:9,propens:[7,8],provid:[3,8,9],py:7,pycausalgp:[1,8,9,10],pypi:10,python3:[10,11],python:[7,10,11],q:4,queri:0,question:[2,7,10],quick:3,quickstart:7,r:[5,8,10],read:10,recent:10,recip:7,recommend:[9,11],refer:6,regist:[7,10],regress:6,regressor:8,reintal:11,remov:[3,11],report:8,repositori:[10,11],repres:6,requir:[9,10,11],restrucuredtext:3,result:[8,9,11],rfunction:7,right:10,rosenbaum:6,rscript:8,rst:3,rubin:6,run:[10,11],s:[8,10],same:8,score:[7,8],search:7,section:[3,8,11],see:[0,3,10],send:2,separ:10,seri:8,session:9,set:7,should:[3,10],simpl:6,size:8,some:11,sourc:8,sqlite:9,standard:6,start:[3,10],step:10,suffici:3,support:[8,11],sure:[10,11],system:[10,11],tab:[2,10],target:8,tbd:[9,10],termin:10,test:11,test_databas:9,thei:3,thi:[3,8,10],through:2,todo:6,top:10,track:9,train:8,training_report:8,treatment:[6,8],tupl:8,two:6,txt:10,type:[8,10],unavail:11,under:[3,9],unreleas:[3,7],up:7,updat:7,us:[3,6,7,8,9,10,11],user:[3,6,9,10,11],valu:[7,8],vector:[6,8],version:[6,10,11],virtual:[10,11],vw:8,w:8,wai:9,want:[10,11],wcorr:8,we:[3,7,9],weight:8,well:11,which:[10,11],whole:7,work:10,written:3,wu:2,x0:8,x1:8,x:8,xgb_train_it:8,xgboost:8,xiao:2,y:10,ye:10,yml:11,you:[10,11],your:[10,11],your_env_nam:11,your_venv:10,your_venv_nam:10},titles:["Changelog","About","Contact Us","Documentation","Frequently Asked Questions","Git Branching Model","Estimating Generalized Propensity Score (GPS)","pycausalgps","Modules","Quickstart","Setting Up Environment","Development Environment"],titleterms:{"new":11,The:9,about:1,ad:0,an:11,ask:4,branch:5,chang:0,changelog:0,commun:7,comput:6,conda:10,contact:2,develop:[7,11],document:3,environ:[10,11],estim:6,exist:11,fix:0,frequent:4,game:9,gener:6,git:5,gp:[6,8],indic:7,instal:[9,11],introduct:7,kernel:11,model:5,modul:8,non:6,packag:9,parametr:6,pipenv:10,propens:6,py:8,pycausalgp:[7,11],question:4,quickstart:9,recip:11,regist:11,remov:0,research:7,rfunction:8,score:6,set:[10,11],tabl:7,unreleas:0,up:[10,11],updat:11,us:2,valu:6,whole:9}})
\ No newline at end of file
+Search.setIndex({docnames:["CHANGELOG","about","contact","documentation","faq","git_branching","index","modules","quickstart","setup_env","setup_env_dev"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,nbsphinx:4,sphinx:56},filenames:["CHANGELOG.md","about.rst","contact.rst","documentation.rst","faq.rst","git_branching.rst","index.rst","modules.rst","quickstart.rst","setup_env.rst","setup_env_dev.rst"],objects:{"pycausalgps.gps":[[7,1,1,"","GeneralizedPropensityScore"]],"pycausalgps.gps.GeneralizedPropensityScore":[[7,2,1,"","_check_data"],[7,2,1,"","_check_params"],[7,2,1,"","_compute_gps"],[7,2,1,"","compute_gps"],[7,2,1,"","compute_gps_xgboost"],[7,2,1,"","get_results"],[7,2,1,"","xgb_train_it"]],"pycausalgps.rscripts":[[7,0,0,"-","rfunctions"]],"pycausalgps.rscripts.rfunctions":[[7,3,1,"","compute_absolute_weighted_corr"],[7,3,1,"","compute_density"]],pycausalgps:[[7,0,0,"-","gps"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:function"},terms:{"0":[6,8],"01":8,"06":6,"07":6,"1":[5,8,9],"10":9,"100":8,"1000":8,"12":[5,8],"1983":[],"2":[5,8,9],"20":8,"200":8,"2004":[],"2022":6,"25":8,"3":[3,5,6,8,9,10],"4":[5,8,9],"42":8,"456":8,"5":[8,9],"500":8,"6":9,"7":10,"class":7,"do":10,"export":10,"float":7,"function":[0,6,7],"import":[3,8,9],"int":7,"new":[2,5,6,9],"return":7,"static":7,"try":8,"while":[5,10],A:[4,5,7,8],As:10,At:9,By:[1,5],If:[8,10],In:[5,8,9],That:9,The:[1,3,5,7,8,9,10],There:[3,8],These:[3,5,8],_:3,__main__:8,__name__:8,_check_data:7,_check_param:7,_compute_gp:7,_short_descript:5,abl:9,about:6,absolut:7,access:5,accord:9,action:3,activ:[9,10],ad:[3,9,10],add:[3,5],addit:[5,8],address:3,adher:5,adjust:[1,6],after:5,aim:5,all:[3,7],allow:5,alpha:5,also:[9,10],alwai:5,an:[2,3,6],anaconda:[9,10],analyz:1,ani:[3,7,8,9,10],answer:4,approach:[0,1,6],ar:[3,5,7,8,9],arang:8,arbitrari:9,arrai:7,articl:5,ask:6,assign:[],attribut:7,auxilari:7,avail:7,backend:8,bandwidth:8,base:[7,8],beta:5,bia:[1,6],bin_seq:8,block:8,bodi:3,both:[1,6],branch:6,braun:2,bug:5,button:9,bw_seq:8,c:9,c_cat:7,c_num:7,calip:[1,6,8],call:[],can:[7,9,10],categor:7,causal:[1,6],cf1:8,cf2:8,cf3:8,cf4:8,cf5:8,cf6:8,chane:10,chang:[3,5],changelog:[3,6],check:7,choos:9,chunk_siz:8,code:[3,5,8,9,10],codebas:5,collabor:5,column:7,command:[3,9,10],commun:5,complet:10,comprehens:1,comput:7,computation:8,compute_absolute_weighted_corr:7,compute_dens:7,compute_gp:7,compute_gps_xgboost:7,computegp:[],conda:[6,10],condit:[],confound:[1,6,7],connect:[],contact:6,contain:7,continu:[1,6,7],contribut:3,contributor:5,control_param:8,convent:5,core:7,corner:9,correct:7,correl:7,counter_weight:8,cova_spec:8,covari:7,covariate_column_cat:[7,8],covariate_column_num:[7,8],creat:[2,5,7,9,10],current:[7,10],daniel:2,data:[1,6,7,9],databas:[],datafram:7,defin:7,densiti:7,depend:10,depth:7,design:1,detail:[0,5,8],develop:[2,3,5,9],dict:7,dictionari:7,differ:8,digit:5,directli:9,directori:9,discuss:2,dist_measur:8,docsourc:3,docstr:3,document:[6,10],download:[9,10],driessen:5,driven:8,dure:3,e:[5,9,10],each:3,easi:5,easier:3,edu:2,effect:[1,6],effici:[1,5],email:2,enabl:1,encount:8,end:3,ensur:1,env:10,environ:[6,8],environment_dev:10,equat:[],erf_help:8,erf_pmetr:8,error:8,estim:[0,1,6,7],estimate_npmetric_erf:8,estimate_pmetric_erf:8,estimate_semipmetric_erf:8,etc:5,evalu:7,exampl:9,exclus:5,exist:[5,6],expens:8,explicitli:1,exposur:[0,1,6,7],exposure_column:[7,8],extend:[],extra:10,facilit:5,famili:8,featur:[5,10],file:[3,10],fire:9,fit:[7,8],fix:[3,5],flag:10,flexibl:[],folder:[3,9,10],follow:[5,7,9,10],folow:7,format:[3,5,7],formula:8,frame:7,framework:[1,6],frequent:6,from:[8,9,10],fulli:6,g:[2,5],gam:8,gaussian:8,gener:[1,2,3,6,7],generalizedpropensityscor:[7,8],generate_syn_pop:8,get:8,get_result:[7,8],git:6,github:[2,9,10],given:[],gnm:8,gp:[0,1,6],gps_data:8,gps_denisti:8,gps_densiti:[7,8],gps_minmax:[7,8],gps_model:[],gps_param:8,gps_spec:8,handl:[],harvard:2,have:[6,9,10],hazard:8,head:5,help:5,here:[5,9],hi:5,higher:10,highli:10,hirano:[],host:5,hotfix:5,hotfix_1:5,howev:[3,9],hypertun:7,imben:[],implement:[1,6,8],implemetn:8,includ:[3,7],increment:5,index:6,indic:7,infer:6,innov:6,input:7,insid:[8,9],instal:[6,9],intern:8,introduc:[1,6],involv:[1,5],ipykernel:[9,10],iss12_add_rot:5,iss:5,issu:[2,5],issue_numb:5,jointli:6,jupyt:[9,10],keep:0,kei:9,kernel:[0,6,7,9],kernelspec:10,kernl:10,khoshnevi:2,l1:8,lab:9,label:9,last:9,latest:[5,10],learn:7,learning_r:[7,8],level:[1,6],lib:[7,8],librari:[1,6,7],lightweight:[9,10],linear:[],link:3,list:[3,7,10],locpol:8,m:[9,10],m_w:8,m_y:8,machin:[],main:[3,5,7],maintain:5,mainten:3,make:[9,10],manag:9,manual:10,match:[0,1,6],mathemat:[],matplotlib:8,max_depth:[7,8],maximum:7,meant:3,methdo:8,method:[1,3,7,8],miniconda:[9,10],minimum:7,mode:[9,10],model:[0,6,7,8],modif:3,modul:[3,6],more:[0,8,9],multipl:5,multiprocess:8,n:10,n_estim:[7,8],n_job:8,n_thread:8,naeem:2,name:[5,7,9,10],navig:[5,9,10],ndarrai:7,necessari:5,need:[3,10],next:9,nkhoshnevi:2,non:0,none:[7,8],nonparametr:[],normal:[0,7],note:[3,8],notebook:[9,10],novel:1,np:[7,8],nsaphx:10,nthread:8,number:[5,7],numer:7,numpi:[7,8],object:7,observ:[1,6],offer:1,one:[5,9],onli:[5,7],open:9,option:7,organ:5,other:9,outcom:1,outcome_sd:8,packag:[5,6,7,9,10],page:[2,6],panda:[7,8],param:[7,8],paramet:7,parametr:0,part:3,particular:[],pass:7,pd:[7,8],pip3:[9,10],pip:8,place:3,plan:10,pleas:[2,3,9,10],plt:8,pmetric:8,polynomi:8,popul:[0,6],popup:9,potenti:1,pre:5,predict:7,predict_al:7,prefix:[5,10],prepar:5,presenc:1,primari:[2,5],primarili:5,privat:3,probabl:[],process:9,product:5,project:[2,5,7,9],propens:[1,6,7],proport:7,propos:5,provid:[1,3,7,8],ps_pop:8,ps_result:8,pseudo:[0,6],pseudo_popul:8,pseudopopul:8,pspop:8,pspop_param:8,put:8,py:6,pycaf:6,pycausalgp:[1,7,8,9],pypi:9,pyplot:8,python3:[9,10],python:[1,6,9,10],q:4,queri:[],question:[2,6,9],quick:3,quickstart:6,r:[7,8,9],random:7,random_st:[7,8],rate:7,ratio:8,read:9,recent:9,recip:6,recommend:10,refer:5,reflect:5,regist:[6,9],regress:8,regressor:7,reintal:10,relationship:1,releas:5,reliabl:1,remov:[3,10],report:7,repositori:[9,10],repres:[],requir:[7,9,10],research:1,respons:[0,6],restrucuredtext:3,result:[7,8,10],rfunction:[6,8],right:9,rosenbaum:[],rotat:5,rscript:[7,8],rst:3,rubin:[],run:[9,10],run_param:8,runtim:8,s:[7,9],same:7,sample_s:8,scale:8,scenario:1,scientist:1,score:[1,6,7],search:6,section:[3,7,10],see:[0,3,8,9],seed_val:8,seek:1,select:8,semi:0,semiparametr:[],send:2,separ:9,seri:[5,7],serv:5,set:[1,6,8],sever:5,should:[3,5,9],simpl:[],simultan:[1,5],size:7,smooth:8,softwar:5,solut:1,some:10,sourc:[5,7],specif:5,specifi:8,spline:8,stabl:5,standard:[],start:[3,5,9],state:7,step:9,str:7,streamlin:5,string:7,success:5,suffici:3,suggest:5,summari:5,support:[5,7,10],sure:[9,10],synthet:6,system:[9,10],tab:[2,9],tailor:1,target:7,temporari:5,termin:9,test:[7,10],test_rat:[7,8],thei:[3,5],thi:[1,3,5,7,9],third:5,three:[5,8],through:2,time:5,to_numpi:8,todo:[],top:9,train:7,training_report:7,treat:8,treatment:[1,7],tree:7,tupl:7,two:8,txt:9,type:[5,7,8,9],typic:5,unavail:10,under:[1,3],unless:5,unreleas:3,up:[6,8],upcom:5,updat:6,us:[0,1,3,5,6,7,8,9,10],user:[3,9,10],util:8,valu:[6,7],variabl:1,vector:7,version:[5,9,10],vincent:5,virtual:[9,10],vw:7,w:7,w_val:8,want:[9,10],wcorr:7,we:[1,3,5,6,8],weight:[0,7],well:10,when:5,where:5,which:[5,9,10],work:[5,9],written:3,wu:2,x0:7,x1:7,x:7,xgb_train_it:7,xgboost:[7,8],xiao:2,y:[8,9],ye:9,yml:10,you:[8,9,10],your:[9,10],your_env_nam:10,your_venv:9,your_venv_nam:9},titles:["Changelog","About","Contact Us","Documentation","Frequently Asked Questions","Git Branching Model","pycausalgps","Modules","Quickstart","Setting Up Environment","Development Environment"],titleterms:{"0":0,"06":0,"07":0,"2022":0,"3":0,"function":8,"new":10,about:1,ad:0,an:10,approach:8,ask:4,branch:5,chang:[],changelog:0,commun:6,comput:[],conda:9,contact:2,data:8,develop:[6,10],document:3,environ:[9,10],estim:8,exampl:8,exist:10,exposur:8,fix:[],frequent:4,gener:8,git:5,gp:[7,8],indic:6,instal:[8,10],introduct:6,kernel:[8,10],match:8,model:5,modul:7,non:[],nonparametr:8,normal:8,packag:8,parametr:8,popul:8,propens:[],pseudo:8,py:7,pycaf:0,pycausalgp:[6,10],question:4,quickstart:8,recip:10,regist:10,remov:[],research:6,respons:8,rfunction:7,score:[],semiparametr:8,set:[9,10],synthet:8,tabl:6,unreleas:[],up:[9,10],updat:10,us:2,valu:8,weight:8}})
\ No newline at end of file
diff --git a/docs/setup_env.html b/docs/setup_env.html
index e843ba8..cc16e55 100644
--- a/docs/setup_env.html
+++ b/docs/setup_env.html
@@ -47,13 +47,9 @@
 <ul class="current">
 <li class="toctree-l1 current"><a class="current reference internal" href="#">Setting Up Environment</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="#conda">Conda</a></li>
-<li class="toctree-l2"><a class="reference internal" href="#pipenv">pipenv</a></li>
 </ul>
 </li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul>
@@ -133,7 +129,7 @@ <h2>Conda<a class="headerlink" href="#conda" title="Permalink to this headline">
 <ul class="simple">
 <li><p>Step 1: Create a virtual environment</p></li>
 </ul>
-<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>conda create --name your_venv <span class="nv">python</span><span class="o">=</span><span class="m">3</span>.7
+<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>conda create --name your_venv <span class="nv">python</span><span class="o">=</span><span class="m">3</span>.10
 </pre></div>
 </div>
 <p>Type ‘y’ (yes) for popup questions.</p>
@@ -143,11 +139,13 @@ <h2>Conda<a class="headerlink" href="#conda" title="Permalink to this headline">
 <div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>conda activate your_venv
 </pre></div>
 </div>
-<p>Install <strong>PyCausalGPS</strong> (Step 3 or Step 4)</p>
+<p>Install <strong>pycausalgps</strong> (Step 3 or Step 4)</p>
 <ul class="simple">
 <li><p>Step 3: Directly from PyPI</p></li>
 </ul>
-<p>TBD</p>
+<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>pip3 install pycausalgps
+</pre></div>
+</div>
 <ul class="simple">
 <li><p>Step 4: Install from Github package in developement mode</p></li>
 </ul>
@@ -189,10 +187,6 @@ <h2>Conda<a class="headerlink" href="#conda" title="Permalink to this headline">
 recently created kernel (in this example: your_venv_name). Choosing a kernel
 will open a new tab that you can work on.</p>
 </div>
-<div class="section" id="pipenv">
-<h2>pipenv<a class="headerlink" href="#pipenv" title="Permalink to this headline"></a></h2>
-<p>TBD</p>
-</div>
 </div>
 
 
diff --git a/docs/setup_env_dev.html b/docs/setup_env_dev.html
index c8ae0d8..15b0025 100644
--- a/docs/setup_env_dev.html
+++ b/docs/setup_env_dev.html
@@ -20,7 +20,7 @@
     <link rel="index" title="Index" href="genindex.html" />
     <link rel="search" title="Search" href="search.html" />
     <link rel="next" title="Documentation" href="documentation.html" />
-    <link rel="prev" title="Frequently Asked Questions" href="faq.html" /> 
+    <link rel="prev" title="Quickstart" href="quickstart.html" /> 
 </head>
 
 <body class="wy-body-for-nav"> 
@@ -47,9 +47,6 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="setup_env.html">Setting Up Environment</a></li>
 <li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html">Estimating Generalized Propensity Score (GPS)</a></li>
-<li class="toctree-l1"><a class="reference internal" href="gps.html#computing-gps-values">Computing GPS Values</a></li>
-<li class="toctree-l1"><a class="reference internal" href="faq.html">Frequently Asked Questions</a></li>
 </ul>
 <p class="caption" role="heading"><span class="caption-text">Developers</span></p>
 <ul class="current">
@@ -173,7 +170,7 @@ <h2>Updating the environment recipe<a class="headerlink" href="#updating-the-env
            </div>
           </div>
           <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
-        <a href="faq.html" class="btn btn-neutral float-left" title="Frequently Asked Questions" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
+        <a href="quickstart.html" class="btn btn-neutral float-left" title="Quickstart" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
         <a href="documentation.html" class="btn btn-neutral float-right" title="Documentation" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
     </div>
 
diff --git a/docsource/CHANGELOG.md b/docsource/CHANGELOG.md
index 0778f92..5340aa9 100644
--- a/docsource/CHANGELOG.md
+++ b/docsource/CHANGELOG.md
@@ -3,16 +3,10 @@
 See [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) for more details.
 
 
-## [Unreleased]
-### Added
-
-- Database class to handle database connection and queries.
+## pycafe 0.0.3 - 2022-06-07
 
-### Changed
-- 
-
-### Fixed 
-- 
+### Added
 
-### Removed
-- 
\ No newline at end of file
+- Exposure response function for parametric, semi-parametric and non-parametric models.
+- Estimation of pseudo-population using weighting and matching approaches.
+- Estimation of GPS using normal and kernel approaches. 
diff --git a/docsource/about.rst b/docsource/about.rst
index 0b66bc7..07b6e33 100644
--- a/docsource/about.rst
+++ b/docsource/about.rst
@@ -1,4 +1,6 @@
 About
 =====
-About pycausalgps ... 
+
+
+pycausalgps is a Python library designed for implementing matching on Generalized Propensity Scores (GPS) in scenarios involving continuous exposures. The approach enables estimating causal effects using observational data tailored explicitly for settings with continuous exposures. By introducing a novel framework for GPS caliper matching, we provide a method that simultaneously matches on both the estimated GPS and exposure levels, ensuring comprehensive adjustment for confounding bias. This library offers a reliable and efficient solution for researchers and data scientists seeking to analyze causal relationships in the presence of continuous treatment variables under potential outcome framework.
 
diff --git a/docsource/git_branching.rst b/docsource/git_branching.rst
index 4d3a2e2..f78178f 100644
--- a/docsource/git_branching.rst
+++ b/docsource/git_branching.rst
@@ -1,4 +1,19 @@
 Git Branching Model
 ===================
 
-Bring it in from the R package ...
\ No newline at end of file
+In this package, we primarily adhere to the successful Git branching model proposed by Vincent Driessen in his article, `A successful Git branching model <https://nvie.com/posts/a-successful-git-branching-model/>`_ proposed by Vincent Driessen. Here are the summary of the branches:
+
+- **main**: The main branch hosts released software packages and is accessible only to project maintainers.
+
+- **develop**: The develop branch serves as the main branch, where the source code at HEAD always reflects the latest development changes for the upcoming release.
+
+- **supporting branches**: Several supporting branches exist, with three primary types for which we suggest contributors follow specific naming conventions:
+
+    + **feature**: Feature branches are created to add new features to the software. Names should follow the format "iss[issue_number]_short_description" (e.g., "iss12_add_rotation" for issue #12, involving the addition of time series rotation). Starting with the issue number allows for easy reference to issue details when necessary. While feature branches are temporary, this naming convention helps developers navigate the codebase efficiently.
+
+    + **hotfix**: Hotfix branches are used exclusively to fix bugs in released packages. After a bug fix, the third digit of the version number should increment by one (e.g., 1.4.2 -> 1.4.3). These branches should be prefixed with "hotfix" and followed by the upcoming version number (e.g., "hotfix_1.4.3").
+
+    + **release**: Release branches facilitate the preparation of new production releases. They are typically not used unless the community is working on multiple releases simultaneously (e.g., pre-alpha, alpha, beta, stable, etc.).
+
+By adhering to this Git branching model, we aim to maintain a streamlined and organized codebase, allowing for efficient collaboration and development.
+
diff --git a/docsource/gps.rst b/docsource/gps.rst
deleted file mode 100644
index c060c7a..0000000
--- a/docsource/gps.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-Estimating Generalized Propensity Score (GPS)
-=============================================
-
-The conditional probability of assignment to a particular treatment given a vector 
-of observed covariates is called propensity score (Rosenbaum and Rubin 1983). 
-The extended version of the propensity score is called the generalized propensity 
-score (Hirano and Imbens 2004). Users can compute a generalized propensity 
-score (GPS) using the `ComputeGPS` class. There are two models implemented to 
-estimate GPS (`gps_models`):
-
-Parametric
----------- 
-
-A parametric model is standard linear regression model. 
-
-TODO: mathematical equation
-
-.. math::
-
-   GPS 
-
-Non-parametric
---------------
-
-A non-parametric model refers to a flexible machine-learning model. 
-
-TODO: mathematical equation
-
-.. math::
-
-   GPS 
-
-
-Computing GPS Values
-====================
-
-The ComputeGPS class can be used to generate GPS values. The following represents
-a simple example.
-
-
-
-
diff --git a/docsource/index.rst b/docsource/index.rst
index 557a563..d1c47c0 100644
--- a/docsource/index.rst
+++ b/docsource/index.rst
@@ -41,9 +41,9 @@ pycausalgps is a Python library for implementing matching on generalized propens
 
    setup_env
    quickstart
-   gps
    faq
 
+
 .. toctree::
    :glob:
    :maxdepth: 2
@@ -63,10 +63,6 @@ pycausalgps is a Python library for implementing matching on generalized propens
    CHANGELOG
 
 
-
-
-
-
 Indices and tables
 ==================
 
diff --git a/docsource/quickstart.rst b/docsource/quickstart.rst
index 1512d2d..4b8ed18 100644
--- a/docsource/quickstart.rst
+++ b/docsource/quickstart.rst
@@ -4,27 +4,411 @@ Quickstart
 Installing the Package
 ----------------------
 
-1. Install the package using pip::
+Install the package using pip:
 
-   *TBD*
+.. code-block:: bash
 
-2. Install the package from Github::
+    $ pip install pycausalgps
 
-   *TBD*
+See the `Setting Up Environment <installation.html>`_ for more details.
 
 
-The Whole Game
---------------
+Generating Synthetic Data
+-------------------------
 
-A processing is described under a project. Although users can use classes individually, we recommend using the project controller because it is the only way to generate a flow of processing and keep track of the results in the database. Each processing session requires an instance of project controller. 
+The package provides a function to generate synthetic data. 
 
 .. code-block:: python
 
-    >>> from pycausalgps.project_controller import ProjectController
-    >>> pc = ProjectController(db_path = 'test_database.sqlite')   
+    >>> from pycausalgps.base.utils import generate_syn_pop
+    >>> data = generate_syn_pop(sample_size=1000, 
+                                seed_val=456, 
+                                outcome_sd=0.25, 
+                                gps_spec=1, 
+                                cova_spec=2)
 
 
-The project controller manages the projects in the database. The project controller will connect to the database if the provided database already exists. Otherwise, it will create a new database. We assume that the database did not exist.
 
+Estimating GPS values
+---------------------
+
+Two approaches are provided to estimate GPS values. These approaches are different 
+based on the type of gps_denisty specified. These approaches are `normal` and `kernel`.
+
+Example of `normal` approach:
++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.base import GeneralizedPropensityScore
+
+    params = {"gps_density": "normal",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps = GeneralizedPropensityScore(data, params)
+
+
+Example of `kernel` approach:
++++++++++++++++++++++++++++++
+
+
+.. code-block:: python
+    
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.base import GeneralizedPropensityScore
+
+    params = {"gps_density": "kernel",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps = GeneralizedPropensityScore(data, params)
+
+
+Generating Pseudo-population
+----------------------------
+
+There are two implemented methdos to generate pseudo-population. These methods
+are: `weighting` and `matching`.
+
+Example of `weighting` approach:
+++++++++++++++++++++++++++++++++
+
+
+.. code-block:: python
+
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps_params = {"gps_density": "normal",
+                  "exposure_column": "treat",
+                  "covariate_column_num": ["cf1", 
+                                           "cf2", 
+                                           "cf3", 
+                                           "cf4", 
+                                           "cf6"],
+                  "covariate_column_cat": ["cf5"],
+                  "libs":{
+                          "xgboost":{
+                                 "n_estimators": 100,
+                                 "max_depth": 3,
+                                 "learning_rate": 0.1,
+                                 "test_rate": 0.2,
+                                 "random_state": 42,
+                                 "n_jobs": 12
+                                 }
+                             }
+    }
+    
+    gps = GeneralizedPropensityScore(data, gps_params)
+    
+    results = gps.get_results()
+    gps_data = {
+        'data' : results.get("data"),
+        'gps_density' : results.get("gps_density")
+        'gps_minmax': results.get("gps_minmax")
+    }
+    
+    pspop_params = {"approach" : "weighting", 
+                    "exposure_column": "treat",
+                    "covariate_column_num": ["cf1", 
+                                             "cf2", 
+                                             "cf3", 
+                                             "cf4", 
+                                             "cf6"],
+                    "covariate_column_cat": ["cf5"],}
+    
+    pspop = PseudoPopulation(data=data, 
+                             gps_data=gps_data, 
+                             params=pspop_params)
+
+
+Example of `matching` approach:
++++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+    
+    
+    # matching
+    gps_params = {"gps_density": "normal",
+                  "exposure_column": "treat",
+                  "covariate_column_num": ["cf1", 
+                                           "cf2", 
+                                           "cf3", 
+                                           "cf4", 
+                                           "cf6"],
+                  "covariate_column_cat": ["cf5"],
+                  "libs":{
+                          "xgboost":{
+                                 "n_estimators": 100,
+                                 "max_depth": 3,
+                                 "learning_rate": 0.1,
+                                 "test_rate": 0.2,
+                                 "random_state": 42,
+                                 "n_jobs": 12
+                                 }
+                             }
+    }
+    
+    data = generate_syn_pop(sample_size=500, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+        
+    gps = GeneralizedPropensityScore(data, gps_params)
+    
+    results = gps.get_results()
+    gps_data = {
+        'data' : results.get("data"),
+        'gps_density' : results.get("gps_density"),
+        'gps_minmax': results.get("gps_minmax")
+    }
+    
+    
+    
+    pspop_params = {"approach" : "matching", 
+                    "exposure_column": "treat",
+                    "covariate_column_num": ["cf1", 
+                                             "cf2", 
+                                             "cf3", 
+                                             "cf4", 
+                                             "cf6"],
+                    "covariate_column_cat": ["cf5"],
+                    "control_params": {"caliper": 1.0,
+                                       "scale": 0.5,
+                                       "dist_measure": "l1",
+                                       "bin_seq": None},
+                    "run_params": {"n_thread": 12,
+                                   "chunk_size": 500},
+    }
+    
+    pspop = PseudoPopulation(data=data, 
+                             gps_data=gps_data, 
+                             params=pspop_params)
+
+
+.. note::
+
+   Note that the matching approach is computationally expensive and internally
+   uses multiprocessing. If you encounter any runtime error, try put the code
+   inside a `if __name__ == '__main__':` block.
+
+
+Exposure Response Function
+--------------------------
+
+The package provides a function to estimate exposure response function. There
+are three implemented methods to estimate exposure response function. These
+methods are: `parametric`, `semiparametric` and `nonparametric`.
+
+Example of `parametric` approach:
++++++++++++++++++++++++++++++++++
+
+The parametric approach estimates the hazard ratios using a parametric
+regression model. In the backend we us gnm R package to implement generalized 
+additive models.
+
+.. code-block:: python
+
+    import pandas as pd
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+    from pycausalgps.rscripts.rfunctions import estimate_pmetric_erf
+
+    gps_params = {"gps_density": "normal",
+              "exposure_column": "treat",
+              "covariate_column_num": ["cf1", 
+                                       "cf2", 
+                                       "cf3", 
+                                       "cf4", 
+                                       "cf6"],
+              "covariate_column_cat": ["cf5"],
+              "libs":{
+                      "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+
+    data = generate_syn_pop(sample_size=200, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+        
+    gps = GeneralizedPropensityScore(data, gps_params)
+
+    results = gps.get_results()
+    gps_data = {
+        'data' : results.get("data"),
+        'gps_density' : results.get("gps_density")
+    }
+
+    pspop_params = {"approach" : "weighting", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                         "cf2", 
+                                         "cf3", 
+                                         "cf4", 
+                                         "cf6"],
+                "covariate_column_cat": ["cf5"],}
+
+    ps_pop = PseudoPopulation(data=data, 
+                              gps_data=gps_data, 
+                              params=pspop_params)
+
+    ps_results = ps_pop.get_results()
+    erf_pmetric = estimate_pmetric_erf(formula="Y ~ treat", 
+                                       family="gaussian", 
+                                       data = ps_results.get("data"))
+
+Example of `semiparametric` approach:
++++++++++++++++++++++++++++++++++++++
+
+The semiparametric approach estimates the smoothed exposure-response function
+using generalized additive model with splines. In the backend we us `gam` R 
+package to implement generalized additive models.
+
+
+.. code-block:: python
+
+    # Code from pmetric approach
+    from pycausalgps.rscripts.rfunctions import estimate_semipmetric_erf
+
+    erf_pmetric = estimate_semipmetric_erf(formula="Y ~ treat", 
+                                           family="gaussian", 
+                                           data = ps_results.get("data"))
+
+
+Example of `nonparametric` approach:
+++++++++++++++++++++++++++++++++++++
+
+The nonparametric approach estimates the smoothed exposure-response function
+using kernel smoothing approach. In the backend we us `locpol` R package to 
+implemetn locpol polynomial fitting with a kernel weight. A data-driven 
+bandwidth selection is implemented. 
+
+
+.. code-block:: python
+    
+    import pandas as pd
+    import numpy as np
+    import matplotlib.pyplot as plt
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+    from pycausalgps.erf_helper import estimate_npmetric_erf
+
+    gps_params = {"gps_density": "normal",
+          "exposure_column": "treat",
+          "covariate_column_num": ["cf1", 
+                                   "cf2", 
+                                   "cf3", 
+                                   "cf4", 
+                                   "cf6"],
+          "covariate_column_cat": ["cf5"],
+          "libs":{
+                  "xgboost":{
+                         "n_estimators": 100,
+                         "max_depth": 3,
+                         "learning_rate": 0.1,
+                         "test_rate": 0.2,
+                         "random_state": 42
+                         }
+                     }
+    }
+
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+
+    gps = GeneralizedPropensityScore(data, gps_params)
+
+    results = gps.get_results()
+
+    gps_data = {
+    'data' : results.get("data"),
+    'gps_density' : results.get("gps_density")
+    }
+
+    pspop_params = {"approach" : "weighting", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                        "cf2", 
+                                        "cf3", 
+                                        "cf4", 
+                                        "cf6"],
+                "covariate_column_cat": ["cf5"],}
+
+    ps_pop = PseudoPopulation(data=data, gps_data=gps_data, params=pspop_params)
+    ps_results = ps_pop.get_results()
+
+    m_Y = ps_results.get("data")["Y"].to_numpy()
+    m_w = ps_results.get("data")["treat"].to_numpy()
+    counter_weight = ps_results.get("data")["counter_weight"].to_numpy()
+    bw_seq = np.arange(0.1, 4.0, 0.01)
+    w_vals = np.arange(1, 20, 0.2)
+    nthread = 12
+
+    erf_pmetric = estimate_npmetric_erf(m_Y, m_w, counter_weight, 
+                                    bw_seq, w_vals, nthread)
 
 
diff --git a/docsource/setup_env.rst b/docsource/setup_env.rst
index a488948..5432c96 100644
--- a/docsource/setup_env.rst
+++ b/docsource/setup_env.rst
@@ -20,7 +20,7 @@ lightweight version of anaconda.
 
 .. code-block:: console
 
-    $ conda create --name your_venv python=3.7
+    $ conda create --name your_venv python=3.10
 
 Type 'y' (yes) for popup questions.
 
@@ -30,11 +30,13 @@ Type 'y' (yes) for popup questions.
 
     $ conda activate your_venv
 
-Install **PyCausalGPS** (Step 3 or Step 4)
+Install **pycausalgps** (Step 3 or Step 4)
 
 - Step 3: Directly from PyPI
 
-TBD
+.. code-block:: console
+
+    $ pip3 install pycausalgps
 
 - Step 4: Install from Github package in developement mode
 
@@ -85,8 +87,4 @@ open terminal and fire up notebook:
 
 At the top right corner, there is a button labeled `New` key. Choose your 
 recently created kernel (in this example: your_venv_name). Choosing a kernel 
-will open a new tab that you can work on.
-
-pipenv
-------
-TBD
\ No newline at end of file
+will open a new tab that you can work on.
\ No newline at end of file
diff --git a/pycausalgps/base/utils.py b/pycausalgps/base/utils.py
index a3f594f..ff0f703 100644
--- a/pycausalgps/base/utils.py
+++ b/pycausalgps/base/utils.py
@@ -39,12 +39,15 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
     seed_val: int 
         A seed value for generating reproducible data.
     outcome_sd: int
-        TBD
+        Standard deviation used to generate the outcome in the synthetic 
+        data set.
     gps_spec: int
-        TBD
+        A numerical value (1-7) that indicates the GPS model used to generate 
+        synthetic data. See the code for more details.
     cova_spec: int
-        TBD
-
+        A numerical value (1-2) to modify the covariates. See the code for 
+        more details.
+        
     Returns
     -------
     data: pd.DataFrame
@@ -55,6 +58,10 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
     100
     """
     
+    if sample_size < 1:
+        raise ValueError("sample_size must be greater than 0")
+
+
     random.seed(seed_val)
 
     mean = [0,0,0,0]
@@ -70,7 +77,6 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
                     np.random.normal(loc=0.0, scale = 5, size=sample_size))
 
     elif gps_spec == 2:
-
         treat = ((- 0.8 + 0.1 * cf[0,:] + 0.1 * cf[1,:] - 0.1 * cf[2,:]
                   + 0.2 * cf[3,:] + 0.1 * cf5 + 0.1 * cf6) * 15 + 22 +
                    np.random.standard_t(size=sample_size,df=2))
@@ -82,7 +88,6 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
         treat = treat['t'].tolist() 
 
     elif gps_spec == 3:
-        
         treat = ((- 0.8 + 0.1 * cf[ 0, :] + 0.1 * cf[ 1, :]- 0.1 *cf[ 2,:] 
            + 0.2 * cf [3, :]
            + 0.1 * cf5 + 0.1 * cf6) * 9
@@ -90,7 +95,6 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
            + np.random.normal(loc=0, scale = 5, size=sample_size) + 15)
 
     elif gps_spec == 4:
-        
         treat = (49 * np.exp((-0.8 + 0.1 * cf[ 0,:] 
                 + 0.1 * cf[ 1, :] - 0.1 * cf[ 2, :]
                 + 0.2 * cf[ 3, :] + 0.1 * cf5 + 0.1 * cf6))
@@ -98,7 +102,6 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
                 + 0.1 * cf[ 1, :] - 0.1 * cf[ 3, :]
                 + 0.2 * cf[ 3, :] + 0.1 * cf5 + 0.1 * cf6))) - 6 
                 + np.random.normal(loc=0, scale=5, size=sample_size))
-        
     elif gps_spec == 5:
         treat = (42 / (1 + np.exp((-0.8 + 0.1 * cf[ 0, :] 
                 + 0.1 * cf[ 1, :]- 0.1 * cf[ 3, :]
@@ -129,24 +132,25 @@ def generate_syn_pop(sample_size: int, seed_val:int, outcome_sd:int,
 
 
     if cova_spec == 1:
-        pass
+        raise NotImplementedError("cova_spec 1 not implemented yet.")
     elif cova_spec == 2:
-        cf[1,:] = np.exp(cf[0,:]/2)
-        cf[2,:] = (cf[1,:]/(1+np.exp(cf[0,:])))+10
-        cf[3,:] = (cf[0,:] * cf[2,:]/25 + 0.6) ^ 3
-        cf[4,:] = (cf[1,:] + cf[3,:] + 20) ^ 2
+        cf[0,:] = np.exp(cf[0,:]/2)
+        cf[1,:] = (cf[1,:]/(1+np.exp(cf[0,:])))+10
+        cf[2,:] = (cf[0,:] * cf[2,:]/25 + 0.6) ** 3
+        cf[3,:] = (cf[1,:] + cf[3,:] + 20) ** 2
     else:
         raise ValueError(f"cova_spec:  {cova_spec} is not a valid value.")
 
     
-    data = pd.DataFrame({'Y':Y, 
-                                   'treat':treat,
-                                   'cf1':cf[0,:],
-                                   'cf2':cf[1,:],
-                                   'cf3':cf[2,:],
-                                   'cf4':cf[3,:],
-                                   'cf5':cf5,
-                                   'cf6':cf6})
+    data = pd.DataFrame({'id': range(1,sample_size+1),
+                         'Y':Y, 
+                         'treat':treat,
+                         'cf1':cf[0,:],
+                         'cf2':cf[1,:],
+                         'cf3':cf[2,:],
+                         'cf4':cf[3,:],
+                         'cf5':cf5,
+                         'cf6':cf6})
 
     return data
 
@@ -162,4 +166,9 @@ def human_readible_size(nbytes):
         nbytes /= 1024.
         i += 1
 
-    return f"{nbytes:.2f} {suffixes[i]}"
\ No newline at end of file
+    return f"{nbytes:.2f} {suffixes[i]}"
+
+
+
+if __name__ == "__main__":
+    data = generate_syn_pop(sample_size=1000, seed_val=456, outcome_sd=0.25, gps_spec=1, cova_spec=2)
\ No newline at end of file
diff --git a/pycausalgps/erf_helper.py b/pycausalgps/erf_helper.py
new file mode 100644
index 0000000..3ac3b32
--- /dev/null
+++ b/pycausalgps/erf_helper.py
@@ -0,0 +1,161 @@
+from scipy.stats import norm
+import numpy as np
+import pandas as pd
+from pycausalgps.log import LOGGER
+from pycausalgps.rscripts.rfunctions import locpol
+from multiprocessing import Pool
+
+
+def generate_kernel(t):
+    return norm.pdf(t)
+
+def w_fun(bw, matched_w, w_vals):
+    w_avals = []
+    for w_val in w_vals:
+        w_std = (matched_w - w_val) / bw
+        kern_std = generate_kernel(w_std) / bw
+        tmp_mean = np.mean(w_std ** 2 * kern_std)
+        w_avals.append(tmp_mean * (generate_kernel(0) / bw) /
+                       (np.mean(kern_std) * tmp_mean - 
+                        np.mean(w_std * kern_std) ** 2))
+    return np.array(w_avals) / len(matched_w)
+
+def estimate_hat_vals(bw, matched_w, w_vals):
+    return np.interp(matched_w, w_vals, w_fun(bw, matched_w, w_vals), right=0)
+
+def smooth_erf(matched_Y, bw, matched_w, matched_cw):
+
+    if sum(matched_cw == 0) == len(matched_cw):
+        matched_cw = matched_cw + 1
+        LOGGER.debug("Giving equal weight for all samples.")
+
+    data = pd.DataFrame({'m_Y': matched_Y, 
+                         'm_w': matched_w, 
+                         'counter_weight': matched_cw})
+    smoothed_val = locpol(data, 'm_Y ~ m_w', bw, matched_w)
+
+    return smoothed_val
+
+
+def compute_risk(h, matched_Y, matched_w, matched_cw, w_vals):
+    hats = estimate_hat_vals(h, matched_w, w_vals)
+    tmp_mean = np.mean(((matched_Y - smooth_erf(
+                         matched_Y,
+                         bw = h,
+                         matched_w = matched_w,
+                         matched_cw = matched_cw)) / (1 - hats)) ** 2)
+    return tmp_mean
+
+def compute_risk_par_helper(args):
+    return compute_risk(*args)
+
+
+def estimate_npmetric_erf(m_Y, m_w, counter_weight, bw_seq, w_vals, nthread):
+    if len(m_Y) != len(m_w):
+        raise ValueError("Length of output and treatment should be equal!")
+
+    if not (isinstance(m_Y, np.ndarray) and isinstance(m_w, np.ndarray)):
+        raise ValueError("Output and treatment vectors should be numpy arrays.")
+
+    if sum(counter_weight == 0) == len(counter_weight):
+        counter_weight = counter_weight + 1
+        LOGGER.debug("Giving equal weight for all samples.")
+    
+    args = [(bw, m_Y, m_w, counter_weight, w_vals) for bw in bw_seq]
+    with Pool(nthread) as pool:
+        risk_val = pool.map(compute_risk_par_helper, args)
+
+    risk_val = np.array(risk_val)
+
+
+    h_opt = bw_seq[np.argmin(risk_val)]
+
+    LOGGER.info(f"The band width with the minimum risk value: {h_opt}.")
+
+    tmp_data = pd.DataFrame({'m_Y': m_Y, 
+                             'm_w': m_w, 
+                             'counter_weight': counter_weight})
+    
+    erf = locpol(tmp_data, 'm_Y ~ m_w', h_opt, w_vals)
+
+    if sum(np.isnan(erf)) > 0:
+        LOGGER.debug(f"erf has {sum(np.isnan(erf))} missing values.")
+
+    result = {
+        'm_Y': m_Y,
+        'm_w': m_w,
+        'bw_seq': bw_seq,
+        'w_vals': w_vals,
+        'risk_val': risk_val,
+        'h_opt': h_opt,
+        'erf': erf,
+    }
+
+    return result
+
+
+
+
+if __name__ == "__main__":
+    import pandas as pd
+
+    from pycausalgps.base.utils import generate_syn_pop
+    from pycausalgps.gps import GeneralizedPropensityScore
+    from pycausalgps.pseudo_population import PseudoPopulation
+
+
+    gps_params = {"gps_density": "normal",
+              "exposure_column": "treat",
+              "covariate_column_num": ["cf1", 
+                                       "cf2", 
+                                       "cf3", 
+                                       "cf4", 
+                                       "cf6"],
+              "covariate_column_cat": ["cf5"],
+              "libs":{
+                      "xgboost":{
+                             "n_estimators": 100,
+                             "max_depth": 3,
+                             "learning_rate": 0.1,
+                             "test_rate": 0.2,
+                             "random_state": 42
+                             }
+                         }
+    }
+
+    data = generate_syn_pop(sample_size=1000, 
+                            seed_val=456, 
+                            outcome_sd=0.25, 
+                            gps_spec=1, 
+                            cova_spec=2)
+    
+    gps = GeneralizedPropensityScore(data, gps_params)
+
+    results = gps.get_results()
+
+    gps_data = {
+    'data' : results.get("data"),
+    'gps_density' : results.get("gps_density")
+    }
+
+    pspop_params = {"approach" : "weighting", 
+                "exposure_column": "treat",
+                "covariate_column_num": ["cf1", 
+                                         "cf2", 
+                                         "cf3", 
+                                         "cf4", 
+                                         "cf6"],
+                "covariate_column_cat": ["cf5"],}
+    
+    ps_pop = PseudoPopulation(data=data, gps_data=gps_data, params=pspop_params)
+    ps_results = ps_pop.get_results()
+
+    m_Y = ps_results.get("data")["Y"].to_numpy()
+    m_w = ps_results.get("data")["treat"].to_numpy()
+    counter_weight = ps_results.get("data")["counter_weight"].to_numpy()
+    bw_seq = np.arange(0.1, 4.0, 0.05)
+    w_vals = np.arange(5, 15, 1)
+    nthread = 1
+
+    erf_pmetric = estimate_npmetric_erf(m_Y, m_w, counter_weight, 
+                                        bw_seq, w_vals, nthread)
\ No newline at end of file
diff --git a/pycausalgps/exposure_response_function.py b/pycausalgps/exposure_response_function.py
new file mode 100644
index 0000000..e1b4ff8
--- /dev/null
+++ b/pycausalgps/exposure_response_function.py
@@ -0,0 +1,128 @@
+"""
+exposure_response_function.py
+=============================
+The core module for the ExposureResponseFunction class.
+"""
+
+from pycausalgps.erf_helper import estimate_npmetric_erf
+from pycausalgps.rscripts.rfunctions import (estimate_pmetric_erf, 
+                                             estimate_semipmetric_erf)
+
+
+
+
+class ExposureResponseFunction:
+    """
+    The ExposureResponseFunction class is the core class for computing the
+    exposure response function. Three types of exposure response functions are
+    supported: parametric, semiparametric, and nonparametric. 
+    """
+
+
+
+    ERF_PARAMETRIC = "parametric"
+    ERF_SEMIPARAMETRIC = "semiparametric"
+    ERF_NONPARAMETRIC = "nonparametric"
+
+
+    def __init__(self, data, params) -> None:
+        self.data = data
+        self.params = params
+        self.outcome = {}
+        self.compute_erf()
+    
+
+    @property
+    def data(self):
+        return self._data
+    
+    @data.setter
+    def data(self, data):
+        self._data = data
+
+    @property
+    def params(self):
+        return self._params
+    
+    @params.setter
+    def params(self, params):
+        self._params = params
+
+    
+    def compute_erf(self):
+
+        erf_type = self.params.get("erf_type")
+
+        if erf_type == self.ERF_PARAMETRIC:
+            output_obj = self._erf_parametric()
+            self.outcome["erf_type"] = erf_type
+            self.outcome["erf_data"] = output_obj.get("data")   
+        elif erf_type == self.ERF_SEMIPARAMETRIC:
+            output_obj = self._erf_semiparametric()
+            self.outcome["erf_type"] = erf_type
+            self.outcome["erf_data"] = output_obj.get("data")
+        elif erf_type == self.ERF_NONPARAMETRIC:
+            output_obj = self._erf_nonparametric()
+        else:
+            raise Exception(f"The provided erf_type ({erf_type}) is not " 
+                            f"supported. Available options: "
+                            f"parametric, semiparametric, or nonparametric.")
+
+    def _erf_parametric(self):
+
+        if "formula" not in self.params:
+            raise Exception("formula is required for parametric erf.")
+        
+        if "family" not in self.params:
+            raise Exception("family is required for parametric erf.")
+
+        result = estimate_pmetric_erf(self.params.get("formula"),
+                                      self.params.get("family"),
+                                      self.data)
+        output_obj = {"data": result}
+        return output_obj
+
+    def _erf_semiparametric(self):
+
+        if "formula" not in self.params:
+            raise Exception("formula is required for semiparametric erf.")
+        
+        if "family" not in self.params:
+            raise Exception("family is required for semiparametric erf.")
+        
+        result = estimate_semipmetric_erf(self.params.get("formula"),
+                                          self.params.get("family"),
+                                          self.data)
+        output_obj = {"data": result}
+        return output_obj
+        
+
+    def _erf_nonparametric(self):
+
+        if "bw_seq" not in self.params:
+            raise Exception("bw_seq is required for nonparametric erf.")
+        
+        if "w_vals" not in self.params:
+            raise Exception("w_vals is required for nonparametric erf.")
+        
+        m_Y = self.data.get("Y").to_numpy()
+        m_w = self.data.get("w").to_numpy()
+        counter_weight = self.data.get("counter_weight").to_numpy()
+        bw_seq = self.params.get("bw_seq")
+        w_vals = self.params.get("w_vals")
+
+        if "nthread" not in self.params:
+            nthread = 1
+        else:
+            nthread = self.params.get("nthread")
+
+        results = estimate_npmetric_erf(m_Y, 
+                                        m_w, 
+                                        counter_weight, 
+                                        bw_seq, 
+                                        w_vals, 
+                                        nthread)
+        
+        return results
+
+
diff --git a/pycausalgps/gps.py b/pycausalgps/gps.py
index aa561a8..dba2508 100644
--- a/pycausalgps/gps.py
+++ b/pycausalgps/gps.py
@@ -25,19 +25,60 @@ class GeneralizedPropensityScore:
 
     Parameters
     ----------
+
     data: pd.DataFrame
         A pandas DataFrame that contains the data for the GPS computation.
     params : dict
-        A dictionary of gps parameters.
+        A dictionary of gps parameters. This includes the folowing required
+        parameters:
+
+            - gps_density: str
+                A string that indicates the density estimation method to be
+                used. Available options are "normal" and "kernel".
+            - exposure_column: str  
+                A string that indicates the name of the exposure column.
+            - covariate_column_num: list
+                A list of strings that indicates the names of the numerical
+                covariate columns.
+            - covariate_column_cat: list
+                A list of strings that indicates the names of the categorical
+                covariate columns.
+            - libs: dict
+                A dictionary of libraries and their parameters. Currently only
+                xgboost is supported. Any parameters for hypertuning the 
+                xgboost model can be passed including:
+
+                    - n_estimators: int
+                        Number of trees to fit.
+                    - learning_rate: float
+                        Learning rate for the xgboost model.
+                    - max_depth: int
+                        Maximum depth of a tree.
+                    - test_rate: float
+                        The proportion of the data to be used for testing.
+                    - random_state: int
+                        Random state for the xgboost model.
+
+    Returns
+    -------
+
+    GeneralizedPropensityScore object that includes the following attributes:
+        - data: pd.DataFrame
+            The gps and auxilary columns.
+        - gps_minmax: list
+            A list of the minimum and maximum gps values.
+        - training_report: dict
+            A dictionary of the training report for the xgboost model.
     """
     
     def __init__(self, data: pd.DataFrame, params: dict) -> None:
-
         self.data = data
         self.params = params
         self.training_report = {}
         self.results = None
-        self.compute_gps()
+        self._check_data()
+        self._check_params()
+        self._compute_gps()
 
     @property
     def data(self) -> pd.DataFrame:
@@ -66,6 +107,27 @@ def __str__(self) -> str:
     def __repr__(self) -> str:
         return (f"GeneralizedPropensityScore(data={self.data},"
                 f"params={self.params})")
+    
+
+    def _check_data(self):
+        """
+        Check if the data is in the correct format.
+        """
+        #TODO: add more checks
+        pass
+
+    def _check_params(self):
+        """
+        Check if the params are in the correct format.
+        """
+
+        required_params = ["gps_density", "exposure_column", 
+                           "covariate_column_num", "covariate_column_cat", 
+                           "libs"]
+        
+        for param in required_params:
+            if not param in self.params.keys():
+                raise ValueError(f"Required parameter {param} is missing.")
 
     def compute_gps(self) -> None:
         """
@@ -73,8 +135,7 @@ def compute_gps(self) -> None:
         parameters.
         """
 
-        libs = nested_get(self.params,
-                          ["gps_params", "libs"])
+        libs = nested_get(self.params, ["libs"])
         
         first_level_keys = list(libs.keys())
         
@@ -94,21 +155,30 @@ def compute_gps(self) -> None:
             
             gps_min, gps_max = gps_res["gps"].min(), gps_res["gps"].max()
             gps_standardized = (gps_res["gps"] - gps_min) / (gps_max - gps_min)    
-            self.results = {"data":pd.DataFrame(
+            results = {"data":pd.DataFrame(
                                        {"id": self.data["id"],
                                         "gps": gps_res["gps"],
                                         "gps_standardized": gps_standardized,
                                         "e_gps_pred": gps_res["e_gps_pred"],
                                         "e_gps_std_pred": gps_res["e_gps_std"],
                                         "w_resid": gps_res["w_resid"]}),
-                                "gps_minmax":[gps_min, gps_max],
-                                "training_report": self.training_report
+                        "gps_minmax":[gps_min, gps_max],
+                        "gps_density": self.params["gps_density"],
+                        "training_report": self.training_report,
             }
-           
+            return results
+
         else:
             LOGGER.warning(f" GPS computing approach (approach): "
                            f" {self.params['approach']}  is not defined.")
+            return None
+
 
+    def _compute_gps(self) -> None:
+        """
+        Compute GPS based on the provided parameters.
+        """
+        self.results = self.compute_gps()
 
     def compute_gps_xgboost(self) -> dict:
         """
@@ -121,21 +191,15 @@ def compute_gps_xgboost(self) -> dict:
         # different parameters without using the class (provided that all information is given).
         
         # preprocess data   
-        num_cols = nested_get(self.params,
-                              ["gps_params", 
-                               "covariate_column_num"])
+        num_cols = nested_get(self.params, ["covariate_column_num"])
         
         X_num = self.data[num_cols]
 
-        exposure_col = nested_get(self.params,
-                                    ["gps_params",
-                                     "exposure_column"])
+        exposure_col = nested_get(self.params, ["exposure_column"])
 
         y = self.data[exposure_col]
  
-        cat_cols = nested_get(self.params,
-                              ["gps_params",
-                               "covariate_column_cat"])
+        cat_cols = nested_get(self.params, ["covariate_column_cat"])
 
         X_cat = self.data[cat_cols]
         X_cat = X_cat.copy()
@@ -163,11 +227,10 @@ def compute_gps_xgboost(self) -> dict:
         X_.loc[:,num_cols] = standard.transform(X_.loc[:,num_cols])       
 
         # get hyperparameters
-        hyper_params = nested_get(self.params,
-                          ["gps_params", "libs", "xgboost"])
+        hyper_params = nested_get(self.params, ["libs", "xgboost"])
 
 
-        if self.params.get("gps_params").get("model") == "parametric":
+        if self.params.get("gps_density") == "normal":
 
             e_gps_pred, training_report = self.xgb_train_it(X_, 
                                                             y.squeeze(), 
@@ -181,7 +244,7 @@ def compute_gps_xgboost(self) -> dict:
                         e_gps_std=e_gps_std,
                         w_resid=None)
 
-        elif self.params.get("gps_params").get("model") == "non-parametric":
+        elif self.params.get("gps_density") == "kernel":
 
             e_gps_pred, training_report = self.xgb_train_it(X_,
                                                             y.squeeze(), 
@@ -206,9 +269,9 @@ def compute_gps_xgboost(self) -> dict:
                         e_gps_std=e_gps_std_pred, 
                         w_resid=w_resid)
         else:
-            LOGGER.warning(f"gps_model: '{self.params['gps_model']}'"
+            LOGGER.warning(f"gps_density: '{self.params['gps_density']}'"
                            f" is not defined."
-                           f" Available models: parametric, non-parametric.")
+                           f" Available options: normal, kernel.")
             return dict()
 
     @staticmethod
@@ -242,6 +305,7 @@ def xgb_train_it(X: pd.DataFrame,
         test_size = params.get("test_rate", 0.2)
         max_depth = params.get("max_depth", 3)
         random_state = params.get("random_state", 42)
+        n_jobs = params.get("n_jobs", 1)
 
         LOGGER.debug(f"XGBoost used parameters: n_estimators={n_estimators}, "
                      f"learning_rate={learning_rate}, test_size={test_size}, "
@@ -250,7 +314,8 @@ def xgb_train_it(X: pd.DataFrame,
 
         xgb = XGBRegressor(n_estimators = n_estimators,
                            learning_rate = learning_rate,
-                           max_depth = max_depth)                 
+                           max_depth = max_depth,
+                           n_jobs=n_jobs)                 
         
         X_train, X_val, y_train, y_val = train_test_split(X, target,
                                     test_size = test_size, 
@@ -281,10 +346,6 @@ def get_results(self) -> dict:
         """
         return self.results
 
-if __name__ == "__main__":
-
-    pass
-
 
 
 
diff --git a/pycausalgps/pseudo_population.py b/pycausalgps/pseudo_population.py
index 48425c7..85bfce7 100644
--- a/pycausalgps/pseudo_population.py
+++ b/pycausalgps/pseudo_population.py
@@ -4,140 +4,122 @@
 The core module for the PseudoPopulation class.
 """
 
-import json
-import hashlib
 from collections import Counter
+from dataclasses import dataclass
+from concurrent.futures import ProcessPoolExecutor
 
 import numpy as np
 import pandas as pd
+from tqdm import tqdm
 import matplotlib.pyplot as plt
 from scipy.stats import gaussian_kde, norm
 from matplotlib.gridspec import GridSpec
 
+
 from pycausalgps.log import LOGGER
-from pycausalgps.database import Database
 from pycausalgps.base.utils import nested_get
 from pycausalgps.rscripts.rfunctions import (compute_density,
                                              compute_absolute_weighted_corr)
 
 class PseudoPopulation:
-
-    def __init__(self, project_params, gps_params, 
-                       pspop_params, db_path) -> None:
-        self.project_params = project_params
-        self.gps_params = gps_params
-        self.pspop_params = pspop_params
-        self.db_path = db_path
-        self.hash_value = None
-        self.pspop_id = None
+    """
+    The PseudoPopulation class is used to compute the pseudo-population.
+
+    Parameters
+    ----------
+
+    data: pd.DataFrame
+        The dataframe containing the data (covariate and exposure columns).
+    gps_data: dict
+        The dictionary containing the GPS data. The required keys are:
+        - gps_data: pd.DataFrame
+            The dataframe containing the GPS data, and its auxiliary columns.
+        - gps_model: str
+    params: dict
+        The dictionary containing the parameters for computing the 
+        pseudo-population. The required parameters are:
+        - approach: str
+            The approach to compute the pseudo-population. The available
+            approaches are: "weighting" and "matching".
+        - exposure_column: str  
+            A string that indicates the name of the exposure column.
+        - covariate_column_num: list
+            A list of strings that indicates the names of the numerical
+            covariate columns.
+        - covariate_column_cat: list
+            A list of strings that indicates the names of the categorical
+            covariate columns.
+
+    """
+
+    APPROACH_WEIGHTING = "weighting"
+    APPROACH_MATCHING = "matching"
+    GPS_DENSITY_NORMAL = "normal"
+    GPS_DENSITY_KERNEL = "kernel"
+
+    def __init__(self, data: pd.DataFrame, gps_data: dict, params: dict):
+
+        self.data = data
+        self.params = params
+        self.gps_data = gps_data
+        self.compiling_report = {}
         self.counter_weight = None
-        self.covariate_balance = None
-        self._generate_hash()
-        self._connect_to_database()
-
-
-    def _generate_hash(self) -> None:
-        """
-        Generate a hash value for the pseudo-population object.
-        """
+        self.generate_pseudo_population()
 
-        hash_string = json.dumps(self.pspop_params, sort_keys=True)
-
-        if self.gps_params.get("hash_value"):
-            hash_string = self.gps_params.get("hash_value") + hash_string
-            self.hash_value = hashlib.sha256(
-                hash_string.encode("utf-8")).hexdigest()
-            # generating random id for the pseudo-population
-            self.pspop_id = hashlib.shake_256(self.hash_value.encode()).hexdigest(8)
-            self.pspop_params["hash_value"] = self.hash_value
-        else:
-            LOGGER.warning("Project hash value is not assigned. " +\
-                           "This can happen becuase of running the class individually. " +\
-                           "Hash value cannot be generated. ")
-
-            
-    def _connect_to_database(self):
-        """
-        Connect to the database.
-        """
-        if self.db_path is None:
-            raise Exception("Database is not defined.")
-            
-        self.db = Database(self.db_path)
+    @property
+    def data(self) -> pd.DataFrame:
+        return self.__data
+    
+    @data.setter
+    def data(self, value: pd.DataFrame) -> None:
+        if not isinstance(value, pd.DataFrame):
+            raise ValueError("Data must be a pandas DataFrame.")
+        self.__data = value
+
+    @property
+    def params(self) -> dict:
+        return self.__params
     
+    @params.setter
+    def params(self, value: dict) -> None:
+        #TODO: Check if the params are in the correct format.
+        if not isinstance(value, dict):
+            raise ValueError("Params must be a dictionary.")
+        self.__params = value
+
+    @property
+    def gps_data(self) -> dict:
+        return self.__gps_data
+    
+    @gps_data.setter
+    def gps_data(self, value: dict) -> None:
+        #TODO: check if the gps_data is in the correct format.
+        if not isinstance(value, dict):
+            raise ValueError("GPS data must be a dictionary.")
+        self.__gps_data = value
+
     def generate_pseudo_population(self) -> None:
         """
         Compute the pseudo-population.
         """
+        self.exposure_data_col_name = self.params.get("exposure_column")
+        self.covariate_col_num = self.params.get("covariate_column_num")
+        self.covariate_col_cat = self.params.get("covariate_column_cat")
         
-        # For computing pspop we need to have the following:
-        # 1. gps_params (gps values, estimated exposure, estimated exposure std,
-        #                w_resid)
-        # 2. pspop_params (approach, caliper, scale, 
-
-        if self.pspop_params.get("pspop_params").get("approach") == "weighting":
-            counter_weight = self._compute_pspop_weighting()
-            self.counter_weight = counter_weight["counter_weight"]
+        if self.params.get("approach") == self.APPROACH_WEIGHTING:
+            self.counter_weight = self._compute_pspop_weighting() 
             self._compute_covariate_balance()
-        elif self.pspop_params.get("pspop_params").get("approach") == "matching":
-            counter_weight = self._compute_pspop_matching()
-            self.counter_weight = counter_weight["counter_weight"]
+        elif self.params.get("approach") == self.APPROACH_MATCHING:
+            (
+             self.counter_weight, 
+             self.counter_weight_list
+            ) = self._compute_pspop_matching()
             self._compute_covariate_balance()
         else:
             raise Exception("Approach is not defined.")
-
-
-    def load_exposure(self) -> None:
-        """
-        Load the exposure values.
-        """
-        exposure_path = self.project_params.get("data").get("exposure_path")
-
-        # load exposure values.
-        exposure_data = pd.read_csv(exposure_path)
-        LOGGER.debug(f"Exposure data shape: {exposure_data.shape}")
-
-        if not isinstance(exposure_data, pd.DataFrame):
-            raise Exception("Exposure data is not a dataframe.")
-
-        if "id" not in exposure_data.columns:
-            raise Exception("Exposure data does not have id column.")
-        
-        return exposure_data
-
-    def load_confounders(self) -> tuple:
-        """
-        Load the confounder values.
-        """
-        covariate_path = self.project_params.get("data").get("covariate_path")
-
-        # load confounder values.
-        covariate_data = pd.read_csv(covariate_path)
-        LOGGER.debug(f"Confounder data shape: {covariate_data.shape}")
-        
-        # select the comlumns that are used in the gps
-        covariate_col_num = nested_get(self.gps_params,
-                                            ["gps_params","pred_model",
-                                            "covariate_column_num"])
-        covariate_col_cat = nested_get(self.gps_params,
-                                            ["gps_params", "pred_model", 
-                                            "covariate_column_cat"])
-        
-        if covariate_col_num is not None:
-            covariate_data_num = covariate_data[covariate_col_num]
-        else:
-            covariate_data_num = None
-        
-        if covariate_col_cat is not None:
-            covariate_data_cat = covariate_data[covariate_col_cat]
-        else:
-            covariate_data_cat = None
-
-
-        return covariate_data_num, covariate_data_cat
-
-
-    def _compute_pspop_weighting(self) -> None:
+    
+    
+    def _compute_pspop_weighting(self) -> pd.DataFrame:
         """
         Compute the pseudo-population using weighting.
         """
@@ -149,36 +131,164 @@ def _compute_pspop_weighting(self) -> None:
         # temp test
         compute_density_with_r = True
         
-        # load exposure values. 
-        exposure_data = self.load_exposure()
-
-        exposure_data_col_name = nested_get(self.gps_params,
-                                                ["gps_params", "pred_model", 
-                                                "exposure_column"])
-
         # compute density of the exposure in the data.
         if compute_density_with_r:
             # compute density with R.
-            w_val = exposure_data[exposure_data_col_name].to_numpy()
+            w_val = self.data[self.exposure_data_col_name].to_numpy()
             data_density = compute_density(w_val, w_val)
             print("Density was computed with R.")
         else:
-            exp_data = exposure_data[exposure_data_col_name]
+            exp_data = self.data[self.exposure_data_col_name]
             kde = gaussian_kde(exp_data)
             data_density = kde(exp_data)
+
         
- 
-        # Extract the gps object from the database.
-        gps_obj = self.db.get_value(self.gps_params.get("hash_value"))
-        
-        ipw = data_density / gps_obj._data.get("_data")["gps"].to_numpy()
-        
-        value = pd.DataFrame({"id": exposure_data["id"], "counter_weight": ipw})
+        ipw = data_density / self.gps_data.get("data")["gps"].to_numpy()            
+        value = pd.DataFrame({"id": self.gps_data.get("data")["id"], 
+                              "counter_weight": ipw})
 
         return value
   
+    @staticmethod
+    def compute_min_idx(i, a, b, scale, c_minus_d):
+        tmp_vals = np.abs(a - b[i]) * scale + c_minus_d
+        min_idx = np.argmin(tmp_vals)
+        return min_idx
+
+    @staticmethod
+    def compute_min_idx_proc(args):
+        i, a, b, scale, c_minus_d = args
+        tmp_vals = np.abs(a - b[i]) * scale + c_minus_d
+        min_idx = np.argmin(tmp_vals)
+        return min_idx
+    
+    @staticmethod
+    def compute_min_idx_proc_chunk(args):
+        start_idx, chunk_size, a, b, scale, c_minus_d = args
+        out_chunk = np.full(chunk_size, -1,  dtype=np.int)  # Initialize with np.nan
+        for i in range(chunk_size):
+            idx = start_idx + i
+            if idx >= len(b):
+                break
+            tmp_vals = np.abs(a - b[idx]) * scale + c_minus_d
+            min_idx = np.argmin(tmp_vals)
+            out_chunk[i] = min_idx
+        return out_chunk
+
+
+    def _process_exposure_level(self, w):
+
+        # load gps object from gps_data
+        gps_min, gps_max = self.gps_data.get("gps_minmax")
+        gps_density = self.gps_data.get("gps_density")
+        if gps_density == self.GPS_DENSITY_KERNEL:
+            w_resid = self.gps_data.get("data")["w_resid"]
+     
+        # load exposure values
+        obs_exposure_data = self.data[["id", self.exposure_data_col_name]]
+        w_min, w_max = (min(obs_exposure_data[self.exposure_data_col_name]), 
+                        max(obs_exposure_data[self.exposure_data_col_name]))
+
+        # controlling parameters
+        delta = nested_get(self.params,
+                                ["control_params", 
+                                "caliper"])
+        scale = nested_get(self.params,
+                                ["control_params",
+                                "scale"])
+        
+        dist_measure = nested_get(self.params, 
+                                 ["control_params", 
+                                  "dist_measure"])
+
+        
+        if gps_density == self.GPS_DENSITY_NORMAL:
+            p_w = norm.pdf(w, 
+                           self.gps_data.get("data")["e_gps_pred"], 
+                           self.gps_data.get("data")["e_gps_std_pred"])
+        elif gps_density == self.GPS_DENSITY_KERNEL:
+            w_new = ((w - self.gps_data.get("data")["e_gps_pred"]) 
+                      / self.gps_data.get("data")["e_gps_std_pred"])
+            p_w = compute_density(w_resid, w_new)
+        else:
+            raise Exception("GPS model is not defined.")
+           
+
+        # select subset of data that are within the caliper value.
+        subset_idx = np.where(np.abs(
+            obs_exposure_data[self.exposure_data_col_name] - w) <= delta)[0]
+        subset_row = obs_exposure_data.iloc[subset_idx]["id"]
+
+        if len(subset_row) == 0:
+            LOGGER.debug(f"No data found within the caliper value ({delta}) " 
+                         f"for the requested exposure level: {w}.")
+            return (w, Counter(None))
+
+
+        # standardize GPS and Exposure values.
+        std_w = (w - w_min)/(w_max - w_min)
+        std_gps = (p_w - gps_min)/(gps_max - gps_min)
+
+        # all data (observational data where w is requested w.)
+        # std_w: scaler
+        # std_gps: vector
+        all_curated_data = pd.DataFrame({"id": obs_exposure_data["id"],
+                                         "std_w": std_w,
+                                         "std_gps": std_gps})
+        
+        # subset of data (actual standardized data that are within the caliper value.)
+        # std_w_subset: vector
+        # std_gps_subset: vector
+        std_w_subset = ((obs_exposure_data[obs_exposure_data["id"].
+                        isin(subset_row)][self.exposure_data_col_name] - w_min)) / (w_max - w_min)
+        
+        # TODO: use query.
+        std_gps_subset = (self.gps_data.get("data")[self.gps_data.get("data")["id"].isin(subset_row)]["gps_standardized"])
+
+        # a: subset of data with standardized GPS (std_gps_subset)
+        # b: all data with estimated GPS based on requested exposure level. (std_gps)
+        # c: subset of data with standardized exposure (std_w_subset)
+        # d: the exposure level that is requested. (std_w)
+        
+        a = std_gps_subset.to_numpy()
+        b = std_gps
+        c = std_w_subset.to_numpy()
+        d = std_w
 
-    def _compute_pspop_matching(self) -> None:
+        c_minus_d = abs(c - d)*(1-scale)
+
+        # compute closest sample from subset of data to these hypothetical samples.
+        # TODO: make this a fucntion
+        len_b = len(b)
+        #len_a = len(a)
+        out = np.zeros(len_b)
+
+        # chunk_size = int(nested_get(self.params, ["run_params", "chunk_size"]))
+        # num_chunks = int(np.ceil(len_b / chunk_size))
+
+        # with ProcessPoolExecutor(max_workers=n_thread) as executor:
+        #     args_list = [(i * chunk_size, chunk_size, a, b, scale, c_minus_d) for i in range(num_chunks)]
+        #     results = list(executor.map(self.compute_min_idx_proc_chunk, args_list))
+
+        # # Flatten the results and assign them back to the 'out' array
+        # out = np.concatenate([chunk[chunk != -1] for chunk in results])
+
+    
+        for i in range(len_b):
+            tmp_vals = np.abs(a - b[i]) * scale + c_minus_d
+            min_idx = np.argmin(tmp_vals)
+            out[i] = min_idx
+
+        # Get the id based on the index.
+        selected_id = subset_row.iloc[out].to_numpy()
+        tmp_freq_table = Counter(selected_id)
+        #counter_weight.update(tmp_freq_table)   
+        
+        return (w, tmp_freq_table)
+    
+
+    
+    def _compute_pspop_matching(self):
 
         """ 
         Compute the pseudo-population using matching approach.
@@ -191,186 +301,98 @@ def _compute_pspop_matching(self) -> None:
         # Caliper value.
         # Scale value.
 
-        
-        # load gps object from the database.
-        gps_obj = self.db.get_value(self.gps_params.get("hash_value"))
-        gps_min, gps_max = gps_obj._data.get("gps_minmax")
-        gps_model = gps_obj.gps_params.get("gps_params").get("model")
-        if gps_model == "non-parametric":
-            w_resid = gps_obj.w_resid
-     
-        # load exposure values
-        obs_exposure_data = self.load_exposure()
-        w_min, w_max = (min(obs_exposure_data["exposure"]), 
-                        max(obs_exposure_data["exposure"]))
-
-        # controlling parameters
-        delta = nested_get(self.pspop_params,
-                               ["pspop_params", 
-                                "controlling_params", 
-                                "caliper"])
-        scale = nested_get(self.pspop_params,
-                                ["pspop_params",
-                                 "controlling_params",
-                                 "scale"])
-        distance_metric = nested_get(self.pspop_params, 
-                                          ["pspop_params",
-                                           "controlling_params", 
-                                           "distance_metric"])
+        obs_exposure_data = self.data[["id", self.exposure_data_col_name]]
+        w_min, w_max = (min(obs_exposure_data[self.exposure_data_col_name]), 
+                max(obs_exposure_data[self.exposure_data_col_name]))
 
         # collect requested exposure level.
-        req_exposure = nested_get(self.pspop_params, 
-                                       ["pspop_params", 
-                                        "controlling_params", 
-                                        "bin_seq"])
+        req_exposure = nested_get(self.params, 
+                                 ["control_params", 
+                                  "bin_seq"])
         
+        delta = nested_get(self.params,
+                                ["control_params", 
+                                "caliper"])
+
         # check if req_exposure is string
         if isinstance(req_exposure, str):
             req_exposure = eval(req_exposure)
 
         if req_exposure is None:
             req_exposure = np.arange(w_min+delta/2, w_max, delta)
-
-
-        for i, w in enumerate(req_exposure):
-            
-            if gps_model == "parametric":
-                p_w = norm.pdf(w, 
-                               gps_obj._data.get("_data")["e_gps_pred"], 
-                               gps_obj._data.get("_data")["e_gps_std_pred"])
-            elif gps_model == "non-parametric":
-                w_new = ((w - gps_obj._data.get("_data")["e_gps_pred"]) 
-                         / gps_obj._data.get("_data")["e_gps_std_pred"])
-                p_w = compute_density(w_resid, w_new)
-            else:
-                raise Exception("GPS model is not defined.")
-           
-
-
-            # select subset of data that are within the caliper value.
-            subset_idx = np.where(np.abs(obs_exposure_data["exposure"] - w) <= delta)[0]
-            subset_row = obs_exposure_data.iloc[subset_idx]["id"]
-
-            # standardize GPS and Exposure values.
-            std_w = (w - w_min)/(w_max - w_min)
-            std_gps = (p_w - gps_min)/(gps_max - gps_min)
-
-            # all data (observational data where w is requested w.)
-            # std_w: scaler
-            # std_gps: vector
-            all_curated_data = pd.DataFrame({"id": obs_exposure_data["id"],
-                                             "std_w": std_w,
-                                             "std_gps": std_gps})
-            
-            # subset of data (actual standardized data that are within the caliper value.)
-            # std_w_subset: vector
-            # std_gps_subset: vector
-            std_w_subset = ((obs_exposure_data[obs_exposure_data["id"].
-                            isin(subset_row)]["exposure"] - w_min)) / (w_max - w_min)
-            
-            # TODO: use query.
-            std_gps_subset = (gps_obj._data.get("_data")[gps_obj.
-                              _data.get("_data")["id"].
-                              isin(subset_row)]["gps_standardized"])
-
-            # a: subset of data with standardized GPS (std_gps_subset)
-            # b: all data with estimated GPS based on requested exposure level. (std_gps)
-            # c: subset of data with standardized exposure (std_w_subset)
-            # d: the exposure level that is requested. (std_w)
-            
-            a = std_gps_subset.to_numpy()
-            b = std_gps
-            c = std_w_subset.to_numpy()
-            d = std_w
-
-            c_minus_d = abs(c - d)*(1-scale)
-
-            # compute closest sample from subset of data to these hypothetical samples.
-            # TODO: make this a fucntion
-            len_b = len(b)
-            len_a = len(a)
-            out = np.zeros(len_b)
-
-            # counter_weight = pd.DataFrame.from_dict({key: 0 for key in 
-            #                                          obs_exposure_data["id"]}, 
-            #                                          orient="index", 
-            #                                          columns=['counter_weight'])
-
-            counter_weight = Counter({key: 0 for key in obs_exposure_data["id"]})
+     
+        counter_weight = Counter({key: 0 for key in obs_exposure_data["id"]})
+        counter_weight_list = []
  
-            # Brute force method.
-            for i in range(len_b):
-                for j in range(len_a):
-
-                    subtract_val = abs(a[j] - b[i])*scale
-
-                    tmp_val = subtract_val + c_minus_d[j]
-
-                    if (j == 0):
-                        min_val = tmp_val
-                        min_idx = j
-                        continue
+        n_thread = int(nested_get(self.params, ["run_params", "n_thread"]))
 
-                    if (tmp_val < min_val):
-                        min_val = tmp_val
-                        min_idx = j
-                out[i] = min_idx
+        with ProcessPoolExecutor(max_workers=n_thread) as executor:
+            results = list(tqdm(executor.map(self._process_exposure_level, 
+                                             req_exposure), 
+                                             total=len(req_exposure), 
+                                             desc="Processing exposure levels"))
             
-            # Solution with numpy, but it will consume more memory.
-            # Keeping this for future reference.
-            out2 = np.argmin(np.abs(np.subtract.outer(a, b))*scale 
-                            + c_minus_d[:, np.newaxis], axis=0)
-
-            
-
-            # Get the id based on the index.
-            selected_id = subset_row.iloc[out].to_numpy()
-            tmp_freq_table = Counter(selected_id)
-            counter_weight.update(tmp_freq_table)   
+        # Process the results and update the counter_weight and counter_weight_list
+        for w, tmp_freq_table in results:
+            counter_weight_list.append(CounterWeightData(w, tmp_freq_table))
+    
+        for i_counter_weight in counter_weight_list:
+            counter_weight.update(i_counter_weight.counter_weight)
+    
 
+        for i_counter_weight in counter_weight_list:
+            counter_weight.update(i_counter_weight.counter_weight)
+        
         counter_weight_dict = dict(counter_weight)
         counter_weight_df = pd.DataFrame(list(counter_weight_dict.items()), 
                                                    columns=["id", "counter_weight"])
 
-        return counter_weight_df
+        return counter_weight_df, counter_weight_list
 
             
-
-
-
     def _compute_covariate_balance(self) -> None:
         """
         Compute covariate balance.
         """
         if self.counter_weight is None:
-            raise Exception("Counter weight is not defined. " +\
-                "Try generating the pseudo-population first.")
+            raise Exception(f"Counter weight is not defined. " 
+                             "Try generating the pseudo-population first.")
 
-        # load exposure values.
-        exposure_data = self.load_exposure()
-        exp_data = exposure_data[self.gps_params.get("gps_params").get("pred_model").get("exposure_column")].to_numpy()
+        
+        # merge the counter weight with the original data.
+        # TODO: warn users if there is a discrepancy in ids.
+        self.merged_data = pd.merge(self.data, self.counter_weight, on="id")
+               
+        # load the exposure data.
+        exp_data = self.merged_data[self.exposure_data_col_name].to_numpy()
 
         # load the confounders.
-        covariate_data_num, covariate_data_cat = self.load_confounders()
+        covariate_data_num = self.merged_data[self.covariate_col_num]
+        covariate_data_cat = self.merged_data[self.covariate_col_cat]
+        counter_weight = self.merged_data["counter_weight"].to_numpy()
 
         # compute weighted correlation between the confounders and the exposure.
         covariate_balance = compute_absolute_weighted_corr(exp_data, 
-                                                           self.counter_weight, 
+                                                           counter_weight, 
                                                            covariate_data_num,
                                                            covariate_data_cat) 
 
-        original_data_covariate_balance = compute_absolute_weighted_corr(exp_data,
-                                                                         np.ones(self.counter_weight.shape),
-                                                                         covariate_data_num,
-                                                                         covariate_data_cat)                    
+        original_data_covariate_balance = compute_absolute_weighted_corr(
+            exp_data,
+            np.ones(counter_weight.shape),
+            covariate_data_num,
+            covariate_data_cat)                    
 
         cov_balance_current = covariate_balance
-        cov_balance_current.rename(columns = {'value':'current'}, inplace = True)
+        cov_balance_current.rename(columns = {'value':'current'}, 
+                                   inplace = True)
         cov_balance_original = original_data_covariate_balance
-        cov_balance_original.rename(columns = {'value':'original'}, inplace = True)
+        cov_balance_original.rename(columns = {'value':'original'}, 
+                                    inplace = True)
 
 
-        cov_balance = pd.merge(cov_balance_current, cov_balance_original, on='name')
+        cov_balance = pd.merge(cov_balance_current, cov_balance_original, 
+                               on='name')
 
         self.covariate_balance = cov_balance
 
@@ -448,18 +470,244 @@ def plot_cov_balance(self) -> None:
         hyper_param_3 = {"fontsize": 8, "ha":"left", "va":"center"}
 
         ax3.text(xlim3[0], ylim3[1]-0.05, 'Data Info', **hyper_param_1)
-        ax3.text(xlim3[0], ylim3[1]-0.15, 
-                 f'GPS Object ID: {self.gps_params.get("gps_id")}', 
-                 **hyper_param_3)
-        ax3.text(xlim3[0], ylim3[1]-0.2,
-                    f'GPS hash value: {self.gps_params.get("hash_value")}',
-                    **hyper_param_3)
-        ax3.text(xlim3[0], ylim3[1]-0.25,
-                    f'Pseudo population ID: {self.pspop_id}',
-                    **hyper_param_3)
-        ax3.text(xlim3[0], ylim3[1]-0.3,
-                    f'Pseudo population hash value: {self.hash_value}',
-                    **hyper_param_3)
+        # ax3.text(xlim3[0], ylim3[1]-0.15, 
+        #          f'GPS Object ID: {self.gps_params.get("gps_id")}', 
+        #          **hyper_param_3)
+        # ax3.text(xlim3[0], ylim3[1]-0.2,
+        #             f'GPS hash value: {self.gps_params.get("hash_value")}',
+        #             **hyper_param_3)
+        # ax3.text(xlim3[0], ylim3[1]-0.25,
+        #             f'Pseudo population ID: {self.pspop_id}',
+        #             **hyper_param_3)
+        # ax3.text(xlim3[0], ylim3[1]-0.3,
+        #             f'Pseudo population hash value: {self.hash_value}',
+        #             **hyper_param_3)
 
         fig.suptitle("Covariate Balance", fontsize=16)
-        plt.show()   
\ No newline at end of file
+        plt.show()   
+
+
+    def get_results(self) -> dict:
+        """
+        Get the results of generating the pseudo-population.
+        """
+
+        results = {"data": self.merged_data,
+                   "params": self.params,
+                   "compiling_report": self.compiling_report,
+                   "covariate_balance": self.covariate_balance}
+        
+        return results
+
+    def get_individual_counter_weight(self) -> list:
+        """
+        Get the counter-weight for each individual exposure.
+        """
+
+        if self.counter_weight_list is None:
+            raise Exception("Counter-weight is not defined. " +\
+                "Try generating the pseudo-population first.")
+
+        return self.counter_weight_list
+
+
+
+
+
+
+@dataclass    
+class CounterWeightData:
+    w: float
+    counter_weight: np.ndarray
+
+    
+    
+    
+    # Old code 
+
+
+
+# def process_exposure_level_2(w, gps_data, data, 
+#                                   exposure_data_col_name, params,
+#                                   GPS_DENSITY_NORMAL, GPS_DENSITY_KERNEL):
+
+#         # load gps object from gps_data
+#         gps_min, gps_max = gps_data.get("gps_minmax")
+#         gps_density = gps_data.get("gps_density")
+#         if gps_density == GPS_DENSITY_KERNEL:
+#             w_resid = gps_data.get("data")["w_resid"]
+     
+#         # load exposure values
+#         obs_exposure_data = data[["id", exposure_data_col_name]]
+#         w_min, w_max = (min(obs_exposure_data[exposure_data_col_name]), 
+#                         max(obs_exposure_data[exposure_data_col_name]))
+
+#         # controlling parameters
+#         delta = nested_get(params,
+#                                 ["control_params", 
+#                                 "caliper"])
+#         scale = nested_get(params,
+#                                 ["control_params",
+#                                 "scale"])
+        
+#         dist_measure = nested_get(params, 
+#                                  ["control_params", 
+#                                   "dist_measure"])
+
+        
+#         if gps_density == GPS_DENSITY_NORMAL:
+#             p_w = norm.pdf(w, 
+#                            gps_data.get("data")["e_gps_pred"], 
+#                            gps_data.get("data")["e_gps_std_pred"])
+#         elif gps_density == GPS_DENSITY_KERNEL:
+#             w_new = ((w - gps_data.get("data")["e_gps_pred"]) 
+#                       / gps_data.get("data")["e_gps_std_pred"])
+#             p_w = compute_density(w_resid, w)
+#         else:
+#             raise Exception("GPS model is not defined.")
+           
+
+#         # select subset of data that are within the caliper value.
+#         subset_idx = np.where(np.abs(
+#             obs_exposure_data[exposure_data_col_name] - w) <= delta)[0]
+#         subset_row = obs_exposure_data.iloc[subset_idx]["id"]
+
+#         if len(subset_row) == 0:
+#             LOGGER.debug(f"No data found within the caliper value ({delta}) " 
+#                          f"for the requested exposure level: {w}.")
+#             return (w, Counter(None))
+
+
+#         # standardize GPS and Exposure values.
+#         std_w = (w - w_min)/(w_max - w_min)
+#         std_gps = (p_w - gps_min)/(gps_max - gps_min)
+
+#         # all data (observational data where w is requested w.)
+#         # std_w: scaler
+#         # std_gps: vector
+#         all_curated_data = pd.DataFrame({"id": obs_exposure_data["id"],
+#                                             "std_w": std_w,
+#                                             "std_gps": std_gps})
+        
+#         # subset of data (actual standardized data that are within the caliper value.)
+#         # std_w_subset: vector
+#         # std_gps_subset: vector
+#         std_w_subset = ((obs_exposure_data[obs_exposure_data["id"].
+#                         isin(subset_row)][exposure_data_col_name] - w_min)) / (w_max - w_min)
+        
+#         # TODO: use query.
+#         std_gps_subset = (gps_data.get("data")[gps_data.get("data")["id"].isin(subset_row)]["gps_standardized"])
+
+#         # a: subset of data with standardized GPS (std_gps_subset)
+#         # b: all data with estimated GPS based on requested exposure level. (std_gps)
+#         # c: subset of data with standardized exposure (std_w_subset)
+#         # d: the exposure level that is requested. (std_w)
+        
+#         a = std_gps_subset.to_numpy()
+#         b = std_gps
+#         c = std_w_subset.to_numpy()
+#         d = std_w
+
+#         c_minus_d = abs(c - d)*(1-scale)
+
+#         # compute closest sample from subset of data to these hypothetical samples.
+#         # TODO: make this a fucntion
+#         len_b = len(b)
+#         #len_a = len(a)
+#         out = np.zeros(len_b)
+
+#         # chunk_size = int(nested_get(self.params, ["run_params", "chunk_size"]))
+        
+#         # num_chunks = int(np.ceil(len_b / chunk_size))
+
+#         # with ProcessPoolExecutor(max_workers=n_thread) as executor:
+#         #     args_list = [(i * chunk_size, chunk_size, a, b, scale, c_minus_d) for i in range(num_chunks)]
+#         #     results = list(executor.map(self.compute_min_idx_proc_chunk, args_list))
+
+#         # # Flatten the results and assign them back to the 'out' array
+#         # out = np.concatenate([chunk[chunk != -1] for chunk in results])
+
+    
+#         for i in range(len_b):
+#             tmp_vals = np.abs(a - b[i]) * scale + c_minus_d
+#             min_idx = np.argmin(tmp_vals)
+#             out[i] = min_idx
+
+#         # Get the id based on the index.
+#         selected_id = subset_row.iloc[out].to_numpy()
+#         tmp_freq_table = Counter(selected_id)
+#         #counter_weight.update(tmp_freq_table)   
+        
+#         return (w, tmp_freq_table)
+    
+
+# if __name__ == "__main__":
+
+#     from pycausalgps.base.utils import generate_syn_pop
+#     from pycausalgps.gps import GeneralizedPropensityScore
+
+#     gps_params = {"gps_density": "normal",
+#                   "exposure_column": "treat",
+#                   "covariate_column_num": ["cf1", 
+#                                            "cf2", 
+#                                            "cf3", 
+#                                            "cf4", 
+#                                            "cf6"],
+#                   "covariate_column_cat": ["cf5"],
+#                   "libs":{
+#                           "xgboost":{
+#                                  "n_estimators": 100,
+#                                  "max_depth": 3,
+#                                  "learning_rate": 0.1,
+#                                  "test_rate": 0.2,
+#                                  "random_state": 42
+#                                  }
+#                              }
+#     }
+    
+#     data = generate_syn_pop(sample_size=1000, 
+#                             seed_val=456, 
+#                             outcome_sd=0.25, 
+#                             gps_spec=1, 
+#                             cova_spec=2)
+        
+#     gps = GeneralizedPropensityScore(data, gps_params)
+#     results = gps.get_results()
+#     gps_data = {
+#         'data' : results.get("data"),
+#         'gps_density' : results.get("gps_density"),
+#         'gps_minmax': results.get("gps_minmax")}
+
+#     # merged_df = pd.merge(data, gps_data, on='id')
+#     # merged_df
+
+#     # pspop_params = {"approach" : "weighting", 
+#     #                 "exposure_column": "treat",
+#     #                 "covariate_column_num": ["cf1", 
+#     #                                          "cf2", 
+#     #                                          "cf3", 
+#     #                                          "cf4", 
+#     #                                          "cf6"],
+#     #                 "covariate_column_cat": ["cf5"]}
+    
+
+#     pspop_params = {"approach" : "matching", 
+#                 "exposure_column": "treat",
+#                 "covariate_column_num": ["cf1", 
+#                                          "cf2", 
+#                                          "cf3", 
+#                                          "cf4", 
+#                                          "cf6"],
+#                 "covariate_column_cat": ["cf5"],
+#                 "control_params": {"caliper": 1.0,
+#                                    "scale": 0.5,
+#                                    "dist_measure": "l1",
+#                                    "bin_seq": None},
+#                 "run_params": {"n_thread": 12,
+#                                "chunk_size": 500}}
+
+#     pspop = PseudoPopulation(data=data, 
+#                             gps_data=gps_data, 
+#                             params=pspop_params)
+    
+
diff --git a/pycausalgps/rscripts/__init__.py b/pycausalgps/rscripts/__init__.py
index 758beb2..3691f66 100644
--- a/pycausalgps/rscripts/__init__.py
+++ b/pycausalgps/rscripts/__init__.py
@@ -9,7 +9,7 @@
 utils.chooseCRANmirror(ind=1) # select the first mirror in the list
 
 # define packages to be installed
-packnames = ('polycor', 'locpol', 'wCorr')
+packnames = ('polycor', 'locpol', 'wCorr', 'gnm', 'gam')
 
 # Install packages if not installed
 names_to_install = [x for x in packnames if not rpackages.isinstalled(x)]
diff --git a/pycausalgps/rscripts/absolute_weighted_corr.r b/pycausalgps/rscripts/absolute_weighted_corr_df.r
similarity index 69%
rename from pycausalgps/rscripts/absolute_weighted_corr.r
rename to pycausalgps/rscripts/absolute_weighted_corr_df.r
index a5d0de1..e0069de 100644
--- a/pycausalgps/rscripts/absolute_weighted_corr.r
+++ b/pycausalgps/rscripts/absolute_weighted_corr_df.r
@@ -9,10 +9,7 @@
 absolute_weighted_corr_df <- function(w,
                                       vw,
                                       c_num,
-                                      c_cat){
-  
-  
- 
+                                      c_cat) {
   # detect numeric columns
   col_n <- colnames(c_num)
 
@@ -24,9 +21,9 @@ absolute_weighted_corr_df <- function(w,
   absolute_corr_n <- absolute_corr_f <- NULL
 
   if (length(col_n) > 0) {
-    absolute_corr_n<- sapply(col_n,function(i){
+    absolute_corr_n <- sapply(col_n, function(i) {
       abs(wCorr::weightedCorr(x = w,
-                              y = c_num[,i],
+                              y = c_num[, i],
                               weights = vw,
                               method = c("spearman")))})
     absolute_corr_n <- unlist(absolute_corr_n)
@@ -34,15 +31,16 @@ absolute_weighted_corr_df <- function(w,
   }
 
   if (length(col_f) > 0) {
-    internal_fun<- function(i){
+    internal_fun<- function(i) {
       abs(wCorr::weightedCorr(x = w,
                               y = c_cat[, i],
                               weights = vw,
-                              method = c("Polyserial")))}
+                              method = c("Polyserial")))
+                              }
 
     absolute_corr_f <- c()
     for (item in col_f){
-      if (length(unique(c_cat[[item]])) == 1 ){
+      if (length(unique(c_cat[[item]])) == 1) {
         absolute_corr_f <- c(absolute_corr_f, NA)
       } else {
         absolute_corr_f <- c(absolute_corr_f, internal_fun(item))
@@ -53,16 +51,17 @@ absolute_weighted_corr_df <- function(w,
 
   absolute_corr <- c(absolute_corr_n, absolute_corr_f)
 
-  if (sum(is.na(absolute_corr)) > 0){
-    warning(paste("The following features generated missing values: ",
-                  names(absolute_corr)[is.na(absolute_corr)],
-                  "\nIn computing mean covariate balance, they will be ignored."))
+  if (sum(is.na(absolute_corr)) > 0) {
+    warning(
+      paste("The following features generated missing values: ",
+            names(absolute_corr)[is.na(absolute_corr)],
+            "\nIn computing mean covariate balance, they will be ignored."))
   }
 
   df <- data.frame(name = character(), value = numeric())
 
   for (i in names(absolute_corr)){
-    df <- rbind(df, data.frame(name=i, value=absolute_corr[[i]]))
+    df <- rbind(df, data.frame(name = i, value = absolute_corr[[i]]))
   }
 
   return(df)
diff --git a/pycausalgps/rscripts/compute_density.r b/pycausalgps/rscripts/compute_density.r
index 8d05ce9..493f0e8 100644
--- a/pycausalgps/rscripts/compute_density.r
+++ b/pycausalgps/rscripts/compute_density.r
@@ -1,4 +1,4 @@
-compute_density <- function(x0, x1){
+compute_density <- function(x0, x1) {
     tmp_density <- stats::density(x0, na.rm = TRUE)
     dnst <- stats::approx(tmp_density$x, tmp_density$y, xout = x1,
                           rule = 2)$y
diff --git a/pycausalgps/rscripts/r_estimate_pmetric_erf.r b/pycausalgps/rscripts/r_estimate_pmetric_erf.r
new file mode 100644
index 0000000..080362a
--- /dev/null
+++ b/pycausalgps/rscripts/r_estimate_pmetric_erf.r
@@ -0,0 +1,37 @@
+r_estimate_pmetric_erf <- function(formula, family, data) {
+
+  if (any(data$counter_weight < 0)){
+    stop("Negative weights are not allowed.")
+  }
+
+  if (sum(data$counter_weight) == 0) {
+    data$counter_weight <- data$counter_weight + 1
+  }
+
+  counter_weight <- data$counter_weight
+
+  formula <- as.formula(formula)
+  gnm_model <- gnm::gnm(formula = formula,
+                        family = family,
+                        data = data,
+                        weights = counter_weight)
+
+  if (is.null(gnm_model)) {
+    stop("gnm model is null. Did not converge.")
+  }
+
+  fitted_values <- gnm_model$fitted.values
+
+  # get the w values based on the formula's second term
+  w_column <- gnm_model$terms[[3]]
+
+  if (is.null(fitted_values)) {
+    vals <- data.frame()
+  } else {
+    vals <- data.frame(w_vals = data[[w_column]],
+                       fitted_values = fitted_values)
+    colnames(vals) <- c("w", "fitted_values")
+  }
+
+  return(vals)
+}
\ No newline at end of file
diff --git a/pycausalgps/rscripts/r_estimate_semipmetric_erf.r b/pycausalgps/rscripts/r_estimate_semipmetric_erf.r
new file mode 100644
index 0000000..f5a76d6
--- /dev/null
+++ b/pycausalgps/rscripts/r_estimate_semipmetric_erf.r
@@ -0,0 +1,36 @@
+r_estimate_semipmetric_erf <- function(formula, family, data) {
+
+
+  if (any(data$counter_weight < 0)){
+    stop("Negative weights are not allowed.")
+  }
+
+  if (sum(data$counter_weight) == 0) {
+    data$counter_weight <- data$counter_weight + 1
+  }
+
+  counter_weight <- data$counter_weight
+
+  formula <- as.formula(formula)
+  gam_model <- gam::gam(formula = formula,
+                        family = family,
+                        data = data,
+                        weights = counter_weight)
+
+  if (is.null(gam_model)) {
+    stop("gnm model is null. Did not converge.")
+  }
+
+  fitted_values <- gam_model$fitted.values
+  w_column <- gam_model$terms[[3]]
+  
+  if (is.null(fitted_values)) {
+    vals <- data.frame()
+  } else {
+    vals <- data.frame(w_vals = data[[w_column]],
+                       fitted_values = fitted_values)
+    colnames(vals) <- c("w", "fitted_values")
+  }
+
+  return(vals)
+}
\ No newline at end of file
diff --git a/pycausalgps/rscripts/r_locpol.r b/pycausalgps/rscripts/r_locpol.r
new file mode 100644
index 0000000..87d40ab
--- /dev/null
+++ b/pycausalgps/rscripts/r_locpol.r
@@ -0,0 +1,13 @@
+
+r_locpol <- function(data, formula, bw, w_vals) {
+  formula <- as.formula(formula)
+  tmp_loc <- locpol::locpol(formula = formula,
+                            data = data,
+                            bw = bw,
+                            weig = data$counter_weight,
+                            xeval = w_vals,
+                            kernel = locpol::gaussK)
+
+  erf <- tmp_loc$lpFit$m_Y
+  return(erf)
+}
\ No newline at end of file
diff --git a/pycausalgps/rscripts/rfunctions.py b/pycausalgps/rscripts/rfunctions.py
index 60269c9..b49628d 100644
--- a/pycausalgps/rscripts/rfunctions.py
+++ b/pycausalgps/rscripts/rfunctions.py
@@ -4,6 +4,7 @@
 Main module to define the R functions to be used in the package.
 """
 
+import os
 
 import numpy as np
 import pandas as pd
@@ -19,15 +20,13 @@
 # Check if function is defined
 # TODO: Check if there is a better way to fix the path.
 if 'compute_density' not in robjects.globalenv:
+    
     try:
-        robjects.r('''
-            compute_density <- function(x0, x1){
-                tmp_density <- stats::density(x0, na.rm = TRUE)
-                dnst <- stats::approx(tmp_density$x, tmp_density$y, xout = x1,
-                                      rule = 2)$y
-            return(dnst)
-           }
-        ''')
+        r_file_path = os.path.join(os.path.dirname(__file__), 
+                                   'compute_density.r')
+
+        robjects.r(f'source("{r_file_path}")')
+
     except Exception as e:
         print('Error in loading the R function: compute_density')
         print(e)
@@ -81,69 +80,12 @@ def compute_density(x0:np.array, x1:np.array) -> np.array:
 # Check if function is defined
 if 'absolute_weighted_corr' not in robjects.globalenv:
     try:
-        robjects.r('''
-            absolute_weighted_corr_df <- function(w,
-                                      vw,
-                                      c_num,
-                                      c_cat){
-  
-  
- 
-                # detect numeric columns
-                col_n <- colnames(c_num)
-              
-                # detect factorial columns
-                col_f <- colnames(c_cat)
-              
-                c_cat[] <- lapply(c_cat, as.factor)
-              
-                absolute_corr_n <- absolute_corr_f <- NULL
-              
-                if (length(col_n) > 0) {
-                  absolute_corr_n<- sapply(col_n,function(i){
-                    abs(wCorr::weightedCorr(x = w,
-                                            y = c_num[,i],
-                                            weights = vw,
-                                            method = c("spearman")))})
-                  absolute_corr_n <- unlist(absolute_corr_n)
-                  names(absolute_corr_n) <- col_n
-                }
-              
-                if (length(col_f) > 0) {
-                  internal_fun<- function(i){
-                    abs(wCorr::weightedCorr(x = w,
-                                            y = c_cat[, i],
-                                            weights = vw,
-                                            method = c("Polyserial")))}
-              
-                  absolute_corr_f <- c()
-                  for (item in col_f){
-                    if (length(unique(c_cat[[item]])) == 1 ){
-                      absolute_corr_f <- c(absolute_corr_f, NA)
-                    } else {
-                      absolute_corr_f <- c(absolute_corr_f, internal_fun(item))
-                    }
-                  }
-                  names(absolute_corr_f) <- col_f
-                }
-              
-                absolute_corr <- c(absolute_corr_n, absolute_corr_f)
-              
-                if (sum(is.na(absolute_corr)) > 0){
-                  warning(paste("The following features generated missing values: ",
-                                names(absolute_corr)[is.na(absolute_corr)],
-                                "\nIn computing mean covariate balance, they will be ignored."))
-                }
-              
-                df <- data.frame(name = character(), value = numeric())
-              
-                for (i in names(absolute_corr)){
-                  df <- rbind(df, data.frame(name=i, value=absolute_corr[[i]]))
-                }
-              
-                return(df)
-            }
-        ''')
+
+        r_file_path = os.path.join(os.path.dirname(__file__), 
+                                   'absolute_weighted_corr_df.r')
+
+        robjects.r(f'source("{r_file_path}")')
+
     except Exception as e:
         print('Error in loading the R function: absolute_weighted_corr')
         print(e)
@@ -199,6 +141,143 @@ def compute_absolute_weighted_corr(w:np.array,
 #### ---------------------------------------------------------------------------
 
 
+### Compute parametric exposure response function ------------------------------
+
+# R
+# TODO: Check if there is a better way to fix the path.
+if 'r_estimate_pmetric_erf' not in robjects.globalenv:
+    
+    try:
+        r_file_path = os.path.join(os.path.dirname(__file__), 
+                                   'r_estimate_pmetric_erf.r')
+
+        robjects.r(f'source("{r_file_path}")')
+
+    except Exception as e:
+        print('Error in loading the R function: compute_density')
+        print(e)
+
+
+# Python
+def estimate_pmetric_erf(formula:str,
+                         family:str,
+                         data:pd.DataFrame) -> any:
+    
+
+    # Convert pandas dataframe to R dataframe
+    with localconverter(robjects.default_converter + pandas2ri.converter):
+        r_data = robjects.conversion.py2rpy(data)
+    
+    # collect the function from R
+    r_estimate_pmetric_erf = robjects.globalenv['r_estimate_pmetric_erf']
+
+    # call the function
+    results = r_estimate_pmetric_erf(formula, 
+                                     family, 
+                                     r_data)
+    
+    # Convert results to python
+    with localconverter(robjects.default_converter + 
+                        robjects.pandas2ri.converter):
+        py_results = robjects.conversion.rpy2py(results)
+
+    return py_results
+    
+#### ---------------------------------------------------------------------------
+
+## Compute semiparametric exposure response function ---------------------------
+
+# R
+# TODO: Check if there is a better way to fix the path.
+if 'r_estimate_semipmetric_erf' not in robjects.globalenv:
+    
+    try:
+        r_file_path = os.path.join(os.path.dirname(__file__), 
+                                   'r_estimate_semipmetric_erf.r')
+
+        robjects.r(f'source("{r_file_path}")')
+
+    except Exception as e:
+        print('Error in loading the R function: compute_density')
+        print(e)
+
+
+# Python
+def estimate_semipmetric_erf(formula:str,
+                             family:str,
+                             data:pd.DataFrame) -> any:
+    
+
+    # Convert pandas dataframe to R dataframe
+    with localconverter(robjects.default_converter + pandas2ri.converter):
+        r_data = robjects.conversion.py2rpy(data)
+    
+    # collect the function from R
+    r_estimate_semipmetric_erf = robjects.globalenv['r_estimate_semipmetric_erf']
+
+    # call the function
+    results = r_estimate_semipmetric_erf(formula, 
+                                         family, 
+                                         r_data)
+    
+    # Convert results to python
+    with localconverter(robjects.default_converter + 
+                        robjects.pandas2ri.converter):
+        py_results = robjects.conversion.rpy2py(results)
+
+    return py_results
+    
+#### ---------------------------------------------------------------------------
+
+
+#### locpol function -----------------------------------------------------------
+
+# R
+if 'r_locpol' not in robjects.globalenv:
+        
+    try:
+        r_file_path = os.path.join(os.path.dirname(__file__), 
+                                'r_locpol.r')
+
+        robjects.r(f'source("{r_file_path}")')
+
+    except Exception as e:
+        print('Error in loading the R function: r_locpol')
+        print(e)
+
+# Python
+
+def locpol(data: pd.DataFrame, formula: str, bw: float, w_vals:np.array) -> any:
+
+    # Convert pandas dataframe to R dataframe
+    with localconverter(robjects.default_converter + pandas2ri.converter):
+        r_data = robjects.conversion.py2rpy(data)
+
+    # Convert numpy array to R vector
+    if not isinstance(w_vals, robjects.vectors.FloatVector):
+        w_vals = robjects.FloatVector(w_vals)
+
+    # Convert bw to R vector
+    if not isinstance(bw, robjects.vectors.FloatVector):
+        bw = robjects.FloatVector([bw])
+
+    # collect the function from R
+    r_locpol = robjects.globalenv['r_locpol']
+
+    # call the function
+    results = r_locpol(data = r_data,
+                       formula = formula, 
+                       bw = bw,
+                       w_vals = w_vals)
+
+    # Convert results to python (R vector to numpy array)
+    with localconverter(robjects.default_converter + 
+                        robjects.pandas2ri.converter):
+        py_results = robjects.conversion.rpy2py(results)
+
+    return py_results
+
+
 
 if __name__ == "__main__":
     x0 = np.random.normal(0, 1, 100)
diff --git a/setup.py b/setup.py
index 867a3d8..4e9a7de 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
 
 setup(
     name="pycausalgps", 
-    version="0.0.2",
+    version="0.0.3",
     author="Naeem Khoshnevis, Xiao Wu, Danielle Braun",
     author_email="nkhoshnevis@g.harvard.edu, xw2892@cumc.columbia.edu, dbraun@mail.harvard.edu",
     maintainer="Naeem Khoshnevis",


Test patch:
diff --git a/tests/test_gps.py b/tests/test_gps.py
index 8a5b681..76da84a 100644
--- a/tests/test_gps.py
+++ b/tests/test_gps.py
@@ -14,8 +14,7 @@ def setUp(self):
                                   "cat_feature": np.random.choice(["A", "B", "C"], 100)})
 
         self.params = {
-            "gps_params": {
-                "model": "parametric",
+                "gps_density": "normal",
                 "exposure_column": ["exposure"],
                 "covariate_column_num": ["num_feature_1", "num_feature_2"],
                 "covariate_column_cat": ["cat_feature"],
@@ -28,7 +27,6 @@ def setUp(self):
                         "random_state": 42
                     }
                 }
-            }
         }
 
     def test_init(self):
diff --git a/tests/test_pseudo_population.py b/tests/test_pseudo_population.py
new file mode 100644
index 0000000..b27bf9d
--- /dev/null
+++ b/tests/test_pseudo_population.py
@@ -0,0 +1,139 @@
+import unittest
+from collections import Counter
+
+import numpy as np
+import pandas as pd
+
+from pycausalgps.pseudo_population import PseudoPopulation, CounterWeightData
+
+
+class TestPseudoPopulation(unittest.TestCase):
+
+    def setUp(self):
+
+        self.data = pd.DataFrame({
+                        "id": range(1, 101),
+                        "exposure": np.random.random(100),
+                        "num_feature_1": np.random.random(100),
+                        "num_feature_2": np.random.random(100),
+                        "cat_feature": np.random.choice(["A", "B", "C"], 100)})
+        
+        gps_data = pd.DataFrame({
+                        "id": range(1, 101),
+                        "gps": np.random.random(100),
+                        "gps_standardized": np.random.random(100),
+                        "e_gps_pred": np.random.random(100),
+                        "e_gps_std_pred": np.random.random(100),
+                        "w_resid": np.random.random(100)})
+        
+        self.gps_data = {
+            'data' : gps_data,
+            'gps_density' : "normal",
+            'gps_minmax': [min(gps_data['gps']), max(gps_data['gps'])]
+        }
+
+        self.params_weighting = {
+            'approach': PseudoPopulation.APPROACH_WEIGHTING,
+            'exposure_column': 'exposure',
+            'covariate_column_num': ['num_feature_1', 'num_feature_2'],
+            'covariate_column_cat': ['cat_feature']}
+        
+        self.params_matching = {
+            'approach': PseudoPopulation.APPROACH_MATCHING,
+            'exposure_column': 'exposure',
+            'covariate_column_num': ['num_feature_1', 'num_feature_2'],
+            'covariate_column_cat': ['cat_feature'], 
+            "control_params": {"caliper": 1.0,
+                                   "scale": 0.5,
+                                   "dist_measure": "l1",
+                                   "bin_seq": None},
+            "run_params": {"n_thread": 12,
+                               "chunk_size": 500}}
+        
+
+    def test_init_weighting(self):
+        pspop = PseudoPopulation(self.data, 
+                                 self.gps_data, 
+                                 self.params_weighting)
+        results = pspop.get_results()
+
+        self.assertIn("data", results)
+        self.assertIn("params", results)
+        self.assertIn("compiling_report", results)
+        self.assertIn("covariate_balance", results)
+        self.assertEqual(results.get("data").shape[0], 100)
+        self.assertEqual(results.get("covariate_balance").shape[0], 3)
+
+    def test_init_matching(self):
+        pspop = PseudoPopulation(self.data, self.gps_data, self.params_matching)
+        results = pspop.get_results()
+
+        self.assertIn("data", results)
+        self.assertIn("params", results)
+        self.assertIn("compiling_report", results)
+        self.assertIn("covariate_balance", results)
+        self.assertEqual(results.get("data").shape[0], 100)
+        self.assertEqual(results.get("covariate_balance").shape[0], 3)
+
+
+    def test_raise_exception_when_counter_weight_not_defined(self):
+        pspop = PseudoPopulation(self.data, self.gps_data, self.params_matching)
+        pspop.counter_weight = None
+        with self.assertRaises(Exception):
+            pspop._compute_covariate_balance()
+
+    def test_merge_data_creattion(self):
+        self.pspop_weighting = PseudoPopulation(self.data, 
+                                                self.gps_data, 
+                                                self.params_weighting)
+        self.pspop_matching = PseudoPopulation(self.data, 
+                                               self.gps_data, 
+                                               self.params_matching)
+        self.pspop_weighting._compute_covariate_balance()
+        self.assertIsNotNone(self.pspop_weighting.merged_data)
+
+        self.pspop_matching._compute_covariate_balance()
+        self.assertIsNotNone(self.pspop_matching.merged_data)
+
+
+    def test_compute_covariate_balance_values(self):
+
+        self.pspop_weighting = PseudoPopulation(self.data, 
+                                                self.gps_data, 
+                                                self.params_weighting)
+        self.pspop_matching = PseudoPopulation(self.data, 
+                                               self.gps_data, 
+                                               self.params_matching)
+        cov_balance_current = self.pspop_weighting.covariate_balance['current']
+        cov_balance_original = self.pspop_weighting.covariate_balance['original']
+        self.assertTrue(np.all(np.abs(cov_balance_current) <= 1))
+        self.assertTrue(np.all(np.abs(cov_balance_original) <= 1))
+
+        cov_balance_current = self.pspop_matching.covariate_balance['current']
+        cov_balance_original = self.pspop_matching.covariate_balance['original']
+        self.assertTrue(np.all(np.abs(cov_balance_current) <= 1))
+        self.assertTrue(np.all(np.abs(cov_balance_original) <= 1))
+
+    def test_covariate_balance_data_frame(self):
+        self.pspop_weighting = PseudoPopulation(self.data, 
+                                                self.gps_data, 
+                                                self.params_weighting)
+        self.pspop_matching = PseudoPopulation(self.data, 
+                                               self.gps_data, 
+                                               self.params_matching)
+        expected_columns = ['name', 'current', 'original']
+        self.assertTrue(
+            set(expected_columns).issubset(
+            self.pspop_weighting.covariate_balance.columns))
+        self.assertTrue(
+            set(expected_columns).issubset(
+            self.pspop_matching.covariate_balance.columns))
+
+
+
+
+
+
+
+if __name__ == "__main__":
+    unittest.main()
\ No newline at end of file
diff --git a/tests/test_utils.py b/tests/test_utils.py
index d15eab3..8b8bcd0 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,6 +1,9 @@
 import unittest
 
+import pandas as pd
+
 from pycausalgps.base.utils import nested_get
+from pycausalgps.base.utils import generate_syn_pop
 
 class TestUtils(unittest.TestCase):
     def test_nested_get(self):
@@ -11,4 +14,69 @@ def test_nested_get(self):
         d = {"a": {"b": {"c": 1}}}
         keys = ["a", "b", "d"]
         self.assertEqual(nested_get(d, keys), None)
-           
\ No newline at end of file
+           
+
+class TestGenerateSynPop(unittest.TestCase):
+
+    def test_valid_gps_spec(self):
+        sample_size = 100
+        seed_val = 42
+        outcome_sd = 1
+        gps_spec = 3
+        cova_spec = 2
+
+        result = generate_syn_pop(sample_size, seed_val, outcome_sd,
+                                  gps_spec, cova_spec)
+        self.assertIsInstance(result, pd.DataFrame)
+        self.assertEqual(len(result), sample_size)
+
+    def test_invalid_gps_spec(self):
+        sample_size = 100
+        seed_val = 42
+        outcome_sd = 1
+        gps_spec = 8  # Invalid gps_spec value
+        cova_spec = 2
+
+        with self.assertRaises(ValueError):
+            generate_syn_pop(sample_size, seed_val, outcome_sd, 
+                             gps_spec, cova_spec)
+
+    def test_valid_cova_spec(self):
+        sample_size = 100
+        seed_val = 42
+        outcome_sd = 1
+        gps_spec = 3
+        cova_spec = 2
+
+        result = generate_syn_pop(sample_size, seed_val, outcome_sd, 
+                                  gps_spec, cova_spec)
+        self.assertIsInstance(result, pd.DataFrame)
+        self.assertEqual(len(result), sample_size)
+
+    def test_invalid_cova_spec(self):
+        sample_size = 100
+        seed_val = 42
+        outcome_sd = 1
+        gps_spec = 3
+        cova_spec = 3  # Invalid cova_spec value
+
+        with self.assertRaises(ValueError):
+            generate_syn_pop(sample_size, seed_val, outcome_sd, 
+                             gps_spec, cova_spec)
+
+    def test_columns(self):
+        sample_size = 100
+        seed_val = 42
+        outcome_sd = 1
+        gps_spec = 3
+        cova_spec = 2
+
+        result = generate_syn_pop(sample_size, seed_val, outcome_sd, 
+                                  gps_spec, cova_spec)
+        expected_columns = ['id', 'Y', 'treat', 'cf1', 'cf2', 'cf3', 
+                            'cf4', 'cf5', 'cf6']
+        self.assertListEqual(list(result.columns), expected_columns)
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file