Repository: pacti-org/pacti (language: python)

Issue:
Dsp wl optimization
1. Updated Notebook to use the figure from the media public repo 
2. Added description and figure in the case_study.md
3. Added some tests extracted from the case study


Discussion / hints:

Fix patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..ecb0fed9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,30 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG] "
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+```py
+code
+```
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**System (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Software version [e.g., Pacti, Python...]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..38ac82ce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: "[Feature request]"
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7a0a6c5e..9313e5bb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,26 +1,34 @@
-#on: [push]
-#
-#name: Test Action
-#
-#jobs:
-#  build:
-#    runs-on: ${{ matrix.os }}
-#    strategy:
-#      fail-fast: false
-#      max-parallel: 4
-#      matrix:
-#        os: [windows-latest, ubuntu-latest, macos-latest]
-#        python-version: ["3.8", "3.9", "3.10"]
-#    steps:
-#      - uses: pdm-project/setup-pdm@main
-#        name: Setup PDM
-#        with:
-#          python-version: ${{ matrix.python-version }}
-#          architecture: x64
-#          enable-pep582: true
-##      - name: Install dependencies
-##        run: pdm install
-##      - name: Format code
-##        run: make format
-##      - name: Test code
-##        run: make test
\ No newline at end of file
+on: [push]
+
+name: Test Action
+
+jobs:
+  build:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      max-parallel: 4
+      matrix:
+        os: [windows-latest, ubuntu-latest, macos-latest]
+        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        steps:
+          - uses: pdm-project/setup-pdm@main
+          name: Setup PDM
+          with:
+            python-version: ${{ matrix.python-version }}
+            architecture: x64
+            enable-pep582: true
+            - name: Install dependencies
+            run: pdm install
+            - name: Test code
+            run: make test
+            - name: Check types
+            run: make check-types
+            - name: Run linters on code
+            run: make check-quality
+            - name: Check types on Jupyter notebooks
+            run: make check-jn-types
+            - name: Run linters on Jupyter notebooks
+            run: make check-jn-quality
+            - name: Check documentation
+            run: make check-docs
diff --git a/.gitignore b/.gitignore
index c3455462..e8967b80 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,6 +73,10 @@ Icon
 
 ./output/*
 
+# VS Code local history
+# https://marketplace.visualstudio.com/items?itemName=xyz.local-history
+.history
+
 # Thumbnails
 ._*
 
diff --git a/LICENSE b/LICENSE
index e8263b1f..a8ddcea0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,6 @@
-This software is licensed under the BSD 2-Clause License.
+BSD 3-Clause License
 
-Copyright (c) 2022, the Pacti contributors.
-All rights reserved.
+Copyright (c) 2023, California Institute of Technology
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
@@ -13,6 +12,10 @@ modification, are permitted provided that the following conditions are met:
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
 
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -22,4 +25,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
index 56e64543..984ff2e1 100644
--- a/Makefile
+++ b/Makefile
@@ -37,8 +37,10 @@ BASIC_DUTIES = \
 
 QUALITY_DUTIES = \
 	check-quality \
+	check-jn-quality \
 	check-docs \
 	check-types \
+	check-jn-types \
 	test
 
 .PHONY: help
@@ -51,7 +53,7 @@ lock:
 
 .PHONY: check
 check:
-	@$(DUTY) check-quality check-types check-docs check-dependencies
+	@$(DUTY) check-quality check-jn-quality check-types check-jn-types check-docs check-dependencies
 
 .PHONY: uninstall
 uninstall:
diff --git a/case_studies/biocircuit_specifications/specification_based_synthetic_biology.ipynb b/case_studies/biocircuit_specifications/specification_based_synthetic_biology.ipynb
index 51e68e5b..f796a6c8 100644
--- a/case_studies/biocircuit_specifications/specification_based_synthetic_biology.ipynb
+++ b/case_studies/biocircuit_specifications/specification_based_synthetic_biology.ipynb
@@ -56,15 +56,16 @@
    "outputs": [],
    "source": [
     "# Load all required libraries\n",
-    "import pandas as pd\n",
+    "from io import StringIO\n",
+    "\n",
     "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
+    "import pandas as pd\n",
     "import sympy\n",
-    "from io import StringIO\n",
-    "\n",
-    "from pacti.terms.polyhedra import PolyhedralContract, write_contract\n",
     "from synbio_utils import display_sensor_contracts, remove_quantization_errors\n",
-    "import pacti.terms.polyhedra as gtp"
+    "\n",
+    "import pacti.terms.polyhedra as gtp\n",
+    "from pacti.terms.polyhedra import PolyhedralContract, write_contract"
    ]
   },
   {
@@ -349,7 +350,7 @@
    ],
    "source": [
     "# Read the data from the paper as a string\n",
-    "data = '''\n",
+    "data = \"\"\"\n",
     "Inducer,Plasmid,Regulator,Promoter,ymax (RPU),ymin (RPUx10-3),K (µM),n,Dynamic range,start,ymax Linear\n",
     "DAPG,pAJM.847,PhlFAM,PPhlF,2.50,2.5,1.700,2.1,1000,0.03,2 \n",
     "Cuma,pAJM.657,CymRAM,PCymRC,3.70,4.3,8.900,2.4,870,0.3,2\n",
@@ -365,7 +366,7 @@
     "OHC14,pAJM.1642,CinRAM,PCin,1.50,3.0,0.430,2.3,490,0.016,0.7\n",
     "Acr,pAJM.884,AcuRAM,PAcu,3.10,37.0,130.000,2.5,83,10,1.7\n",
     "Ery,pAJM.969,MphRAM,PMph,0.29,8.0,65.000,1.5,36,2,0.18\n",
-    "'''\n",
+    "\"\"\"\n",
     "df = pd.read_csv(StringIO(data))\n",
     "\n",
     "df"
@@ -386,12 +387,12 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def create_sensor_contracts(sensor_input=\"AHL\", output=\"FP\", K=0.0, yleak=0.0, start = 0.0, ymax_lin = 0.0):\n",
+    "def create_sensor_contracts(sensor_input=\"AHL\", output=\"FP\", K=0.0, yleak=0.0, start=0.0, ymax_lin=0.0):\n",
     "    \"\"\"\n",
     "    Creates the contracts for a Marionette sensing subsystem\n",
     "    params:\n",
     "        * input (str): The inducer input to the sensor\n",
-    "        * output (str): The output of the genetic construct. Inducer activates the \n",
+    "        * output (str): The output of the genetic construct. Inducer activates the\n",
     "                        production of this output\n",
     "        * K (float): The value of the Hill activation parameter K\n",
     "        * yleak (float): The minimum expression of output even in absence of inducer\n",
@@ -399,66 +400,35 @@
     "        * ymax_lin (float): The maximum expression of output by the inducer\n",
     "                            before saturating (the end of linear regime)\n",
     "    \"\"\"\n",
-    "    slope = (ymax_lin - yleak)/(K - start)\n",
-    "    intercept = yleak - slope*start\n",
+    "    slope = (ymax_lin - yleak) / (K - start)\n",
+    "    intercept = yleak - slope * start\n",
     "    contract_0 = {\n",
-    "            \"_comment\": \"contract when sensor is OFF\",\n",
-    "            \"input_vars\":[\n",
-    "                sensor_input\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{sensor_input:1}, # \"1*input <= start\"\n",
-    "                \"constant\":start}\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:1},\n",
-    "                \"constant\":yleak}\n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"contract when sensor is OFF\",\n",
+    "        \"input_vars\": [sensor_input],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [{\"coefficients\": {sensor_input: 1}, \"constant\": start}],  # \"1*input <= start\"\n",
+    "        \"guarantees\": [{\"coefficients\": {output: 1}, \"constant\": yleak}],\n",
+    "    }\n",
     "    contract_lin = {\n",
-    "            \"_comment\": \"contract for sensor in linear regime\",\n",
-    "            \"input_vars\":[\n",
-    "                sensor_input\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{sensor_input:1},\n",
-    "                \"constant\":K},\n",
-    "                {\"coefficients\":{sensor_input:-1},\n",
-    "                \"constant\":-start}\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:-1, sensor_input:slope},\n",
-    "                \"constant\":intercept},\n",
-    "                {\"coefficients\":{output:1, sensor_input:-slope},\n",
-    "                \"constant\":-intercept}\n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"contract for sensor in linear regime\",\n",
+    "        \"input_vars\": [sensor_input],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {sensor_input: 1}, \"constant\": K},\n",
+    "            {\"coefficients\": {sensor_input: -1}, \"constant\": -start},\n",
+    "        ],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {output: -1, sensor_input: slope}, \"constant\": intercept},\n",
+    "            {\"coefficients\": {output: 1, sensor_input: -slope}, \"constant\": -intercept},\n",
+    "        ],\n",
+    "    }\n",
     "    contract_max = {\n",
-    "            \"_comment\": \"contract for sensor in saturation\",\n",
-    "            \"input_vars\":[\n",
-    "                sensor_input\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{sensor_input:-1},\n",
-    "                \"constant\":-K}\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:-1},\n",
-    "                \"constant\":-ymax_lin}\n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"contract for sensor in saturation\",\n",
+    "        \"input_vars\": [sensor_input],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [{\"coefficients\": {sensor_input: -1}, \"constant\": -K}],\n",
+    "        \"guarantees\": [{\"coefficients\": {output: -1}, \"constant\": -ymax_lin}],\n",
+    "    }\n",
     "    return contract_0, contract_lin, contract_max"
    ]
   },
@@ -483,20 +453,20 @@
    "outputs": [],
    "source": [
     "# Create all sensor contracts:\n",
-    "sensor_names = [str(i) for i in df['Inducer']]\n",
+    "sensor_names = [str(i) for i in df[\"Inducer\"]]\n",
     "sensor_library = {}\n",
     "sensor_library_params = {}\n",
     "for sensor in sensor_names:\n",
     "    sensor_params = {}\n",
-    "    yleak_s = df.loc[df[\"Inducer\"] == sensor][\"ymin (RPUx10-3)\"].values[0]*1e-3\n",
+    "    yleak_s = df.loc[df[\"Inducer\"] == sensor][\"ymin (RPUx10-3)\"].values[0] * 1e-3\n",
     "    s_start = df.loc[df[\"Inducer\"] == sensor][\"start\"].values[0]\n",
     "    s_K = df.loc[df[\"Inducer\"] == sensor][\"K (µM)\"].values[0]\n",
     "    ymax_s = df.loc[df[\"Inducer\"] == sensor][\"ymax Linear\"].values[0]\n",
-    "    sensor_params = {\"leak\":yleak_s, \"start\":s_start, \"K\":s_K, \"ymax\":ymax_s}\n",
+    "    sensor_params = {\"leak\": yleak_s, \"start\": s_start, \"K\": s_K, \"ymax\": ymax_s}\n",
     "    s_output = \"dCas9\"\n",
-    "    contract_s_0, contract_s_lin, contract_s_max = create_sensor_contracts(sensor_input=sensor, output=\"xRFP\", \n",
-    "                                                                           start=s_start, K=s_K,\n",
-    "                                                                           ymax_lin=ymax_s, yleak=yleak_s)\n",
+    "    contract_s_0, contract_s_lin, contract_s_max = create_sensor_contracts(\n",
+    "        sensor_input=sensor, output=\"xRFP\", start=s_start, K=s_K, ymax_lin=ymax_s, yleak=yleak_s\n",
+    "    )\n",
     "    sensor_library[sensor] = [contract_s_0, contract_s_lin, contract_s_max]\n",
     "    sensor_library_params[sensor] = sensor_params"
    ]
@@ -519,21 +489,31 @@
     }
    ],
    "source": [
-    "fig, all_ax = plt.subplots(nrows=3, ncols=5, figsize=(32,20))\n",
+    "fig, all_ax = plt.subplots(nrows=3, ncols=5, figsize=(32, 20))\n",
     "index = 0\n",
     "for sensor in sensor_names:\n",
-    "    ax = all_ax[index//5][index%5]\n",
+    "    ax = all_ax[index // 5][index % 5]\n",
     "    index += 1\n",
-    "    yleak_s = df.loc[df[\"Inducer\"] == sensor][\"ymin (RPUx10-3)\"].values[0]*1e-3\n",
+    "    yleak_s = df.loc[df[\"Inducer\"] == sensor][\"ymin (RPUx10-3)\"].values[0] * 1e-3\n",
     "    s_start = df.loc[df[\"Inducer\"] == sensor][\"start\"].values[0]\n",
     "    s_K = df.loc[df[\"Inducer\"] == sensor][\"K (µM)\"].values[0]\n",
     "    ymax_s = df.loc[df[\"Inducer\"] == sensor][\"ymax Linear\"].values[0]\n",
-    "    ax = display_sensor_contracts(sensor_input=sensor, output=\"\", leak=yleak_s,\n",
-    "                                  start=s_start, K=s_K, ymax_lin=ymax_s,\n",
-    "                                  xlim_min=10**-3, xlim_max=10**4, ylim_min=10**-3, ylim_max=10**2,\n",
-    "                                  ax=ax, show=False)\n",
+    "    ax = display_sensor_contracts(\n",
+    "        sensor_input=sensor,\n",
+    "        output=\"\",\n",
+    "        leak=yleak_s,\n",
+    "        start=s_start,\n",
+    "        K=s_K,\n",
+    "        ymax_lin=ymax_s,\n",
+    "        xlim_min=10**-3,\n",
+    "        xlim_max=10**4,\n",
+    "        ylim_min=10**-3,\n",
+    "        ylim_max=10**2,\n",
+    "        ax=ax,\n",
+    "        show=False,\n",
+    "    )\n",
     "    ax.set_xlabel(sensor, fontsize=16)\n",
-    "    ax.tick_params(axis = 'both', which = 'major', labelsize = 16)\n",
+    "    ax.tick_params(axis=\"both\", which=\"major\", labelsize=16)\n",
     "    ax.set_ylabel(\"Output\", fontsize=16)\n",
     "    ax.legend(fontsize=16)\n",
     "# plt.savefig('all_sensors.svg')\n",
@@ -556,50 +536,32 @@
    "outputs": [],
    "source": [
     "dCas9_contract_off = {\n",
-    "            \"_comment\": \"xRFP:OFF, dCas9:OFF, RFP:ON\",\n",
-    "            \"input_vars\":[\n",
-    "                \"xRFP\", \"dCas9\"\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                \"RFP\"\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{\"xRFP\":1},\n",
-    "                \"constant\":0.2}, # xRFP < 0.2\n",
-    "                {\"coefficients\":{\"dCas9\":1},\n",
-    "                \"constant\":0.1}, # dCas9 < 0.1\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{\"RFP\":-1},\n",
-    "                 \"constant\":-10}, # RFP >= 10\n",
-    "            ]\n",
-    "        }\n",
+    "    \"_comment\": \"xRFP:OFF, dCas9:OFF, RFP:ON\",\n",
+    "    \"input_vars\": [\"xRFP\", \"dCas9\"],\n",
+    "    \"output_vars\": [\"RFP\"],\n",
+    "    \"assumptions\": [\n",
+    "        {\"coefficients\": {\"xRFP\": 1}, \"constant\": 0.2},  # xRFP < 0.2\n",
+    "        {\"coefficients\": {\"dCas9\": 1}, \"constant\": 0.1},  # dCas9 < 0.1\n",
+    "    ],\n",
+    "    \"guarantees\": [\n",
+    "        {\"coefficients\": {\"RFP\": -1}, \"constant\": -10},  # RFP >= 10\n",
+    "    ],\n",
+    "}\n",
     "\n",
     "dCas9_contract_on = {\n",
-    "            \"_comment\": \"The dCas9 repression: xRFP:ON, dCas9:ON, RFP:OFF\",\n",
-    "            \"input_vars\":[\n",
-    "                \"xRFP\", \"dCas9\"\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                \"RFP\"\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{\"xRFP\":-1},\n",
-    "                \"constant\":-0.3}, # xRFP > 0.3\n",
-    "                {\"coefficients\":{\"xRFP\":1},\n",
-    "                \"constant\":1}, # xRFP < 1\n",
-    "                {\"coefficients\":{\"dCas9\":-1},\n",
-    "                \"constant\":-0.1}, # dCas9 > 0.1\n",
-    "                {\"coefficients\":{\"dCas9\":1},\n",
-    "                \"constant\":0.8}, # dCas9 < 0.8\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{\"RFP\":1, \"xRFP\":2, \"dCas9\":0.1},\n",
-    "                 \"constant\":5}, # RFP < 5 - 2xRFP - 0.1dCas9\n",
-    "            ]\n",
-    "        }"
+    "    \"_comment\": \"The dCas9 repression: xRFP:ON, dCas9:ON, RFP:OFF\",\n",
+    "    \"input_vars\": [\"xRFP\", \"dCas9\"],\n",
+    "    \"output_vars\": [\"RFP\"],\n",
+    "    \"assumptions\": [\n",
+    "        {\"coefficients\": {\"xRFP\": -1}, \"constant\": -0.3},  # xRFP > 0.3\n",
+    "        {\"coefficients\": {\"xRFP\": 1}, \"constant\": 1},  # xRFP < 1\n",
+    "        {\"coefficients\": {\"dCas9\": -1}, \"constant\": -0.1},  # dCas9 > 0.1\n",
+    "        {\"coefficients\": {\"dCas9\": 1}, \"constant\": 0.8},  # dCas9 < 0.8\n",
+    "    ],\n",
+    "    \"guarantees\": [\n",
+    "        {\"coefficients\": {\"RFP\": 1, \"xRFP\": 2, \"dCas9\": 0.1}, \"constant\": 5},  # RFP < 5 - 2xRFP - 0.1dCas9\n",
+    "    ],\n",
+    "}"
    ]
   },
   {
@@ -627,13 +589,16 @@
     "for sensor in sensor_library:\n",
     "    in1_params = sensor_library_params[sensor]\n",
     "    in2 = \"aTc\"\n",
-    "    in2_params= sensor_library_params[in2]\n",
-    "    c_atc_off, c_atc_on, c_atc_sat = create_sensor_contracts(sensor_input=\"aTc\", output=\"dCas9\", \n",
-    "                                                             start=in2_params[\"start\"],\n",
-    "                                                             K=in2_params[\"K\"],\n",
-    "                                                             ymax_lin=in2_params[\"ymax\"],\n",
-    "                                                             yleak=in2_params[\"leak\"])\n",
-    "    if sensor != \"aTc\":  \n",
+    "    in2_params = sensor_library_params[in2]\n",
+    "    c_atc_off, c_atc_on, c_atc_sat = create_sensor_contracts(\n",
+    "        sensor_input=\"aTc\",\n",
+    "        output=\"dCas9\",\n",
+    "        start=in2_params[\"start\"],\n",
+    "        K=in2_params[\"K\"],\n",
+    "        ymax_lin=in2_params[\"ymax\"],\n",
+    "        yleak=in2_params[\"leak\"],\n",
+    "    )\n",
+    "    if sensor != \"aTc\":\n",
     "        try:\n",
     "            # OFF condition\n",
     "            c_sensor1 = PolyhedralContract.from_dict(sensor_library[sensor][0])\n",
@@ -643,8 +608,8 @@
     "            top_level_off = remove_quantization_errors(top_level_off)\n",
     "            sym_expr = gtp.PolyhedralTerm.to_symbolic(top_level_off.g.terms[0])\n",
     "            rfp_vals_on = {}\n",
-    "            updated_sym = sym_expr.subs(sensor, 0.9*in1_params[\"start\"])\n",
-    "            updated_sym = updated_sym.subs(in2, 0.9*in2_params[\"start\"])\n",
+    "            updated_sym = sym_expr.subs(sensor, 0.9 * in1_params[\"start\"])\n",
+    "            updated_sym = updated_sym.subs(in2, 0.9 * in2_params[\"start\"])\n",
     "            subs_val = sympy.solve(updated_sym, \"RFP\")\n",
     "            rfp_vals_on[in1_params[\"start\"], in2_params[\"start\"]] = subs_val[0]\n",
     "            # ON condition\n",
@@ -655,11 +620,11 @@
     "            top_level_on = remove_quantization_errors(top_level_on)\n",
     "            sym_expr = gtp.PolyhedralTerm.to_symbolic(top_level_on.g.terms[0])\n",
     "            rfp_vals_off = {}\n",
-    "            updated_sym = sym_expr.subs(sensor, 0.9*in1_params[\"K\"])\n",
-    "            updated_sym = updated_sym.subs(in2, 0.9*in2_params[\"K\"])\n",
+    "            updated_sym = sym_expr.subs(sensor, 0.9 * in1_params[\"K\"])\n",
+    "            updated_sym = updated_sym.subs(in2, 0.9 * in2_params[\"K\"])\n",
     "            subs_val = sympy.solve(updated_sym, \"RFP\")\n",
     "            rfp_vals_off[in1_params[\"K\"], in2_params[\"K\"]] = subs_val[0]\n",
-    "            fold_change[sensor] = list(rfp_vals_on.values())[0]/list(rfp_vals_off.values())[0]\n",
+    "            fold_change[sensor] = list(rfp_vals_on.values())[0] / list(rfp_vals_off.values())[0]\n",
     "            if fold_change[sensor] > desired_fold_change:\n",
     "                sensor_ok[sensor] = True\n",
     "            else:\n",
@@ -668,7 +633,7 @@
     "            if \"unsatisfiable in context\" in str(e):\n",
     "                sensor_ok[sensor] = False\n",
     "            else:\n",
-    "                print(str(e))\n"
+    "                print(str(e))"
    ]
   },
   {
@@ -690,7 +655,7 @@
    ],
    "source": [
     "fig, ax = plt.subplots()\n",
-    "ax.bar(fold_change.keys(), fold_change.values(), color='C1', alpha=0.5)\n",
+    "ax.bar(fold_change.keys(), fold_change.values(), color=\"C1\", alpha=0.5)\n",
     "\n",
     "ax.set_xlabel(\"Sensor\", fontsize=14)\n",
     "ax.set_ylabel(\"Top-level fold-change\", fontsize=14)\n",
@@ -791,15 +756,15 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "yleak_sal = df.loc[df[\"Inducer\"] == \"Sal\"][\"ymin (RPUx10-3)\"].values[0]*1e-3\n",
+    "yleak_sal = df.loc[df[\"Inducer\"] == \"Sal\"][\"ymin (RPUx10-3)\"].values[0] * 1e-3\n",
     "sal_start = df.loc[df[\"Inducer\"] == \"Sal\"][\"start\"].values[0]\n",
     "sal_K = df.loc[df[\"Inducer\"] == \"Sal\"][\"K (µM)\"].values[0]\n",
     "ymax_sal = df.loc[df[\"Inducer\"] == \"Sal\"][\"ymax Linear\"].values[0]\n",
     "\n",
     "sal_output = \"xRFP\"\n",
-    "contract_sal_min, contract_sal_lin, contract_sal_max = create_sensor_contracts(sensor_input=\"Sal\", output=sal_output, \n",
-    "                                                                               start=sal_start, K=sal_K,\n",
-    "                                                                               ymax_lin=ymax_sal, yleak=yleak_sal)"
+    "contract_sal_min, contract_sal_lin, contract_sal_max = create_sensor_contracts(\n",
+    "    sensor_input=\"Sal\", output=sal_output, start=sal_start, K=sal_K, ymax_lin=ymax_sal, yleak=yleak_sal\n",
+    ")"
    ]
   },
   {
@@ -820,9 +785,18 @@
     }
    ],
    "source": [
-    "display_sensor_contracts(sensor_input=\"Input 1\", output=\"xRFP\", leak=yleak_sal,\n",
-    "                         start=sal_start, K=sal_K, ymax_lin=ymax_sal,\n",
-    "                         xlim_min=10**-3, xlim_max=10**3, ylim_min=10**-3, ylim_max=10**1);"
+    "display_sensor_contracts(\n",
+    "    sensor_input=\"Input 1\",\n",
+    "    output=\"xRFP\",\n",
+    "    leak=yleak_sal,\n",
+    "    start=sal_start,\n",
+    "    K=sal_K,\n",
+    "    ymax_lin=ymax_sal,\n",
+    "    xlim_min=10**-3,\n",
+    "    xlim_max=10**3,\n",
+    "    ylim_min=10**-3,\n",
+    "    ylim_max=10**1,\n",
+    ");"
    ]
   },
   {
@@ -840,14 +814,14 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "yleak_atc = df.loc[df[\"Inducer\"] == \"aTc\"][\"ymin (RPUx10-3)\"].values[0]*1e-3\n",
+    "yleak_atc = df.loc[df[\"Inducer\"] == \"aTc\"][\"ymin (RPUx10-3)\"].values[0] * 1e-3\n",
     "atc_start = df.loc[df[\"Inducer\"] == \"aTc\"][\"start\"].values[0]\n",
     "atc_K = df.loc[df[\"Inducer\"] == \"aTc\"][\"K (µM)\"].values[0]\n",
-    "ymax_atc =df.loc[df[\"Inducer\"] == \"aTc\"][\"ymax Linear\"].values[0]\n",
+    "ymax_atc = df.loc[df[\"Inducer\"] == \"aTc\"][\"ymax Linear\"].values[0]\n",
     "atc_output = \"dCas9\"\n",
-    "contract_atc_min, contract_atc_lin, contract_atc_max = create_sensor_contracts(sensor_input=\"aTc\", output=atc_output, \n",
-    "                                                                               start=atc_start, K=atc_K,\n",
-    "                                                                               ymax_lin=ymax_atc, yleak=yleak_atc)"
+    "contract_atc_min, contract_atc_lin, contract_atc_max = create_sensor_contracts(\n",
+    "    sensor_input=\"aTc\", output=atc_output, start=atc_start, K=atc_K, ymax_lin=ymax_atc, yleak=yleak_atc\n",
+    ")"
    ]
   },
   {
@@ -868,9 +842,18 @@
     }
    ],
    "source": [
-    "display_sensor_contracts(sensor_input=\"aTc\", output=\"dCas9\", leak=yleak_atc,\n",
-    "                         start=atc_start, K=atc_K, ymax_lin=ymax_atc,\n",
-    "                         xlim_min=10**-3, xlim_max=10**0, ylim_min=10**-3, ylim_max=10**1);"
+    "display_sensor_contracts(\n",
+    "    sensor_input=\"aTc\",\n",
+    "    output=\"dCas9\",\n",
+    "    leak=yleak_atc,\n",
+    "    start=atc_start,\n",
+    "    K=atc_K,\n",
+    "    ymax_lin=ymax_atc,\n",
+    "    xlim_min=10**-3,\n",
+    "    xlim_max=10**0,\n",
+    "    ylim_min=10**-3,\n",
+    "    ylim_max=10**1,\n",
+    ");"
    ]
   },
   {
@@ -890,8 +873,11 @@
    "source": [
     "leak_RFP = np.max([yleak_atc, yleak_sal])\n",
     "max_RFP = np.max([ymax_atc, ymax_sal])\n",
-    "def create_top_level_contracts(input1 = \"u1\", input2 = \"u2\", output=\"y\", \n",
-    "                               input1_params = None, input2_params = None, output_params = None):\n",
+    "\n",
+    "\n",
+    "def create_top_level_contracts(\n",
+    "    input1=\"u1\", input2=\"u2\", output=\"y\", input1_params=None, input2_params=None, output_params=None\n",
+    "):\n",
     "    \"\"\"\n",
     "    Creates the top level system contracts\n",
     "    \"\"\"\n",
@@ -905,96 +891,60 @@
     "    y_leak = output_params[\"leak\"]\n",
     "    all_contracts = []\n",
     "    contract_top_level_on1 = {\n",
-    "            \"_comment\": \"u1:OFF, u2:OFF, y:ON\",\n",
-    "            \"input_vars\":[\n",
-    "                input1, input2\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{input1:1},\n",
-    "                \"constant\":u1_start - u1_start*0.01},\n",
-    "                {\"coefficients\":{input1:1},\n",
-    "                \"constant\":u2_start - u2_start*0.01}\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:-1},\n",
-    "                \"constant\":-y_max},\n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"u1:OFF, u2:OFF, y:ON\",\n",
+    "        \"input_vars\": [input1, input2],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {input1: 1}, \"constant\": u1_start - u1_start * 0.01},\n",
+    "            {\"coefficients\": {input1: 1}, \"constant\": u2_start - u2_start * 0.01},\n",
+    "        ],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {output: -1}, \"constant\": -y_max},\n",
+    "        ],\n",
+    "    }\n",
     "    all_contracts.append(contract_top_level_on1)\n",
     "    contract_top_level_on2 = {\n",
-    "            \"_comment\": \"u1:ON, u2:OFF, y:ON\",\n",
-    "            \"input_vars\":[\n",
-    "                input1, input2\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{input1:-1},\n",
-    "                \"constant\":-(u1_start + u2_start*0.01)},\n",
-    "                {\"coefficients\":{input1:1},\n",
-    "                \"constant\":u1_K - u1_K*0.01},\n",
-    "                {\"coefficients\":{input2:1},\n",
-    "                \"constant\":u2_start - u2_start*0.01}\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:-1},\n",
-    "                \"constant\":-y_max},\n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"u1:ON, u2:OFF, y:ON\",\n",
+    "        \"input_vars\": [input1, input2],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {input1: -1}, \"constant\": -(u1_start + u2_start * 0.01)},\n",
+    "            {\"coefficients\": {input1: 1}, \"constant\": u1_K - u1_K * 0.01},\n",
+    "            {\"coefficients\": {input2: 1}, \"constant\": u2_start - u2_start * 0.01},\n",
+    "        ],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {output: -1}, \"constant\": -y_max},\n",
+    "        ],\n",
+    "    }\n",
     "    all_contracts.append(contract_top_level_on2)\n",
     "    contract_top_level_on3 = {\n",
-    "            \"_comment\": \"u1:OFF, u2:ON, y:ON\",\n",
-    "            \"input_vars\":[\n",
-    "                input1, input2\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{input1:1},\n",
-    "                \"constant\":u1_start - u1_start*0.01},\n",
-    "                {\"coefficients\":{input2:-1},\n",
-    "                \"constant\":-(u2_start + u2_start*0.01)},\n",
-    "                {\"coefficients\":{input2:1},\n",
-    "                \"constant\":u2_K - u2_K*0.01},\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:-1},\n",
-    "                \"constant\":-y_max},\n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"u1:OFF, u2:ON, y:ON\",\n",
+    "        \"input_vars\": [input1, input2],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {input1: 1}, \"constant\": u1_start - u1_start * 0.01},\n",
+    "            {\"coefficients\": {input2: -1}, \"constant\": -(u2_start + u2_start * 0.01)},\n",
+    "            {\"coefficients\": {input2: 1}, \"constant\": u2_K - u2_K * 0.01},\n",
+    "        ],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {output: -1}, \"constant\": -y_max},\n",
+    "        ],\n",
+    "    }\n",
     "    all_contracts.append(contract_top_level_on3)\n",
     "    contract_top_level_off = {\n",
-    "            \"_comment\": \"u1:ON, u2:ON, y:OFF\",\n",
-    "            \"input_vars\":[\n",
-    "                input1, input2\n",
-    "            ],\n",
-    "            \"output_vars\":[\n",
-    "                output\n",
-    "            ],\n",
-    "            \"assumptions\":\n",
-    "            [\n",
-    "                {\"coefficients\":{input1:-1},\n",
-    "                \"constant\":-(u1_start + u1_start*0.01)},\n",
-    "                {\"coefficients\":{input1:1},\n",
-    "                \"constant\":u1_K - u1_K*0.01},\n",
-    "                {\"coefficients\":{input2:-1},\n",
-    "                \"constant\":-(u2_start + u2_start*0.01)},\n",
-    "                {\"coefficients\":{input2:1},\n",
-    "                \"constant\":u2_K - u2_K*0.01},\n",
-    "            ],\n",
-    "            \"guarantees\":[\n",
-    "                {\"coefficients\":{output:1},\n",
-    "                \"constant\":y_leak},   \n",
-    "            ]\n",
-    "        }\n",
+    "        \"_comment\": \"u1:ON, u2:ON, y:OFF\",\n",
+    "        \"input_vars\": [input1, input2],\n",
+    "        \"output_vars\": [output],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {input1: -1}, \"constant\": -(u1_start + u1_start * 0.01)},\n",
+    "            {\"coefficients\": {input1: 1}, \"constant\": u1_K - u1_K * 0.01},\n",
+    "            {\"coefficients\": {input2: -1}, \"constant\": -(u2_start + u2_start * 0.01)},\n",
+    "            {\"coefficients\": {input2: 1}, \"constant\": u2_K - u2_K * 0.01},\n",
+    "        ],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {output: 1}, \"constant\": y_leak},\n",
+    "        ],\n",
+    "    }\n",
     "    all_contracts.append(contract_top_level_off)\n",
     "    return all_contracts"
    ]
@@ -1006,15 +956,17 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "sal_params = {\"start\":sal_start, \"K\":sal_K}\n",
-    "atc_params = {\"start\":atc_start, \"K\":atc_K}\n",
-    "RFP_params = {\"max\":max_RFP, \"leak\":leak_RFP}\n",
-    "ctop_on1, ctop_on2, ctop_on3, ctop_off = create_top_level_contracts(input1 = \"Sal\",\n",
-    "                                                                    input2 = \"aTc\",\n",
-    "                                                                    output = \"RFP\",\n",
-    "                                                                    input1_params=sal_params,\n",
-    "                                                                    input2_params=atc_params,\n",
-    "                                                                    output_params=RFP_params)"
+    "sal_params = {\"start\": sal_start, \"K\": sal_K}\n",
+    "atc_params = {\"start\": atc_start, \"K\": atc_K}\n",
+    "RFP_params = {\"max\": max_RFP, \"leak\": leak_RFP}\n",
+    "ctop_on1, ctop_on2, ctop_on3, ctop_off = create_top_level_contracts(\n",
+    "    input1=\"Sal\",\n",
+    "    input2=\"aTc\",\n",
+    "    output=\"RFP\",\n",
+    "    input1_params=sal_params,\n",
+    "    input2_params=atc_params,\n",
+    "    output_params=RFP_params,\n",
+    ")"
    ]
   },
   {
@@ -1024,9 +976,15 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "csal_OFF, csal_ON, csal_SAT = tuple(PolyhedralContract.from_dict(el) for el in [contract_sal_min, contract_sal_lin, contract_sal_max])\n",
-    "catc_OFF, catc_ON, catc_SAT = tuple(PolyhedralContract.from_dict(el) for el in [contract_atc_min, contract_atc_lin, contract_atc_max])\n",
-    "ctop_off_off, ctop_on_off, ctop_off_on, ctop_on_on = tuple(PolyhedralContract.from_dict(el) for el in [ctop_on1, ctop_on2, ctop_on3, ctop_off])"
+    "csal_OFF, csal_ON, csal_SAT = tuple(\n",
+    "    PolyhedralContract.from_dict(el) for el in [contract_sal_min, contract_sal_lin, contract_sal_max]\n",
+    ")\n",
+    "catc_OFF, catc_ON, catc_SAT = tuple(\n",
+    "    PolyhedralContract.from_dict(el) for el in [contract_atc_min, contract_atc_lin, contract_atc_max]\n",
+    ")\n",
+    "ctop_off_off, ctop_on_off, ctop_off_on, ctop_on_on = tuple(\n",
+    "    PolyhedralContract.from_dict(el) for el in [ctop_on1, ctop_on2, ctop_on3, ctop_off]\n",
+    ")"
    ]
   },
   {
diff --git a/case_studies/biocircuit_specifications/synbio_utils.py b/case_studies/biocircuit_specifications/synbio_utils.py
index 6366b97c..76f0d844 100644
--- a/case_studies/biocircuit_specifications/synbio_utils.py
+++ b/case_studies/biocircuit_specifications/synbio_utils.py
@@ -1,33 +1,53 @@
 import matplotlib.pyplot as plt
 import numpy as np
-def display_sensor_contracts(sensor_input="u", output="y", leak=0.0, start=0.0, umin=0.0, ymin=0.0, 
-                             K=0.0, ymax_lin=0.0, xlim_min=0.0, xlim_max=0.0,
-                             ylim_min=0.0, ylim_max=0.0, show=True, ax=None):
+
+
+def display_sensor_contracts(
+    sensor_input="u",
+    output="y",
+    leak=0.0,
+    start=0.0,
+    umin=0.0,
+    ymin=0.0,
+    K=0.0,
+    ymax_lin=0.0,
+    xlim_min=0.0,
+    xlim_max=0.0,
+    ylim_min=0.0,
+    ylim_max=0.0,
+    show=True,
+    ax=None,
+):
     """
     Plot three contracts: lag, linear, saturation on a 2-D plane
     """
     if ax is None:
         _, ax = plt.subplots()
-    slope = (ymax_lin - leak)/(K - start)
-    intercept = leak - slope*start
-    ax.hlines(y=leak, xmin=xlim_min, xmax=start, color='r', lw=2, ls='--', label='OFF')
-    ax.axvline(x=start, ls='dotted', color='k')
-    ax.plot(np.linspace(start, K,2), slope*np.linspace(start,K,2) + intercept, color='#006400', lw=2, label='Linear')
-    ax.hlines(y=ymax_lin, xmin=K, xmax=xlim_max, color='blue', lw=2, ls='--', label='Saturation')
-    ax.axvline(x=K, ls='dotted', color='k')
+    slope = (ymax_lin - leak) / (K - start)
+    intercept = leak - slope * start
+    ax.hlines(y=leak, xmin=xlim_min, xmax=start, color="r", lw=2, ls="--", label="OFF")
+    ax.axvline(x=start, ls="dotted", color="k")
+    ax.plot(
+        np.linspace(start, K, 2), slope * np.linspace(start, K, 2) + intercept, color="#006400", lw=2, label="Linear"
+    )
+    ax.hlines(y=ymax_lin, xmin=K, xmax=xlim_max, color="blue", lw=2, ls="--", label="Saturation")
+    ax.axvline(x=K, ls="dotted", color="k")
     ax.set_xlabel(sensor_input, fontsize=14)
-    ax.set_xscale('log')
+    ax.set_xscale("log")
     ax.set_xlim(xlim_min, xlim_max)
     ax.set_ylabel(output, fontsize=14)
-    ax.set_yscale('log')
+    ax.set_yscale("log")
     ax.set_ylim(ylim_min, ylim_max)
     ax.legend()
     if show:
         plt.show()
     return ax
 
+
 import copy
-def remove_quantization_errors(contract, tolerance = 1e-4):
+
+
+def remove_quantization_errors(contract, tolerance=1e-4):
     """
     Removes terms that have all coefficients lower than tolerance
     """
@@ -50,4 +70,4 @@ def remove_quantization_errors(contract, tolerance = 1e-4):
         if all_coeff < tolerance:
             from_contract.g.terms.remove(t_i)
         index += 1
-    return from_contract
\ No newline at end of file
+    return from_contract
diff --git a/case_studies/digital_signal_processing/dsp_wl.ipynb b/case_studies/digital_signal_processing/dsp_wl.ipynb
index c946d23a..a35b56eb 100644
--- a/case_studies/digital_signal_processing/dsp_wl.ipynb
+++ b/case_studies/digital_signal_processing/dsp_wl.ipynb
@@ -20,7 +20,7 @@
     "The fixed-point number with fewer bits tends to have lower accuracy as all bits in the fractional parts exceeding its fractional bits have to be discarded.\n",
     "\n",
     "The following figure shows a multiplication operation using fixed-point numbers.\n",
-    "![Data_Path](./figures/digital_data_path_example.png)\n",
+    "![Data_Path](https://github.com/FormalSystems/media/blob/main/case_studies/digital_signal_processing/digital_data_path_example.png?raw=True)\n",
     "\n",
     "As a result, verifying and optimizing word length for fixed-point implementation is crucial for integrated circuit implementations.\n",
     "To prevent overflow and ensure the inaccuracy is acceptable, we need to bound the error of the system given the wordlength. \n",
@@ -58,9 +58,12 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
-    "from tool import *\n",
-    "import numpy as np"
+    "\"\"\"DSP Case Study Code\"\"\"\n",
+    "import itertools\n",
+    "\n",
+    "from tool import PortWordLength, float_to_bin, port_add, port_mult\n",
+    "\n",
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -82,10 +85,14 @@
     "# Definition of value_num method in PortWordLength class\n",
     "#    def value_num(self):\n",
     "#        return int(self.value, base=2) * 2 ** (- (self.n - self.p))\n",
+    "def show_wordlength() -> None:\n",
+    "    \"\"\"Show the wordlength structure\"\"\"\n",
+    "    port = PortWordLength(n=5, p=2, value=\"11011\")\n",
+    "    print(f'The word length (n = {port.n}, p = {port.p}) of the bits \"{port.value}\" is: ')\n",
+    "    print(port.value_num())\n",
+    "\n",
     "\n",
-    "port = PortWordLength(n=5, p=2, value=\"11011\")\n",
-    "print(f\"The word length (n = {port.n}, p = {port.p}) of the bits \\\"{port.value}\\\" is: \")\n",
-    "print(port.value_num())"
+    "show_wordlength()"
    ]
   },
   {
@@ -106,7 +113,7 @@
     "This error comes from the measurement in the analog signal because of noise and non-ideal effect on the sensor and could be seen as a random noise on the analog signal.\n",
     "\n",
     "The following figure shows the three types of error in the example.\n",
-    "![Error_Type](./figures/type_of_error.png)\n",
+    "![Error_Type](https://github.com/FormalSystems/media/blob/main/case_studies/digital_signal_processing/type_of_error.png?raw=True)\n",
     "\n",
     "Given a input analog signal, the input signal and filter coefficients are first quantized into fixed-point numbers.\n",
     "Then the arithmetic operations, based on the algorithm or filter design, are performed on these fixed-point numbers.\n",
@@ -173,33 +180,50 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def truncation_error_same_position(pi, po):\n",
-    "    assert(pi.p == po.p)\n",
+    "def truncation_error_same_position(pi: PortWordLength, po: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Truncation error for the case where input ports and output ports have the same number of integer bits.\n",
+    "\n",
+    "    Args:\n",
+    "        pi: PortWordLength: input port word length\n",
+    "        po: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The maximum truncation error if a number is truncated from wordlength pi to wordlength po\n",
+    "    \"\"\"\n",
+    "    assert pi.p == po.p\n",
     "    if pi.n > po.n:\n",
-    "        return 2 ** po.p * (2 ** (-po.n) - 2 ** (-pi.n))\n",
-    "    else :\n",
-    "        return 0\n",
+    "        return float(2**po.p * (2 ** (-po.n) - 2 ** (-pi.n)))\n",
+    "    return 0\n",
     "\n",
-    "def truncate(pi, po):\n",
+    "\n",
+    "def truncate(pi: PortWordLength, po: PortWordLength) -> None:\n",
+    "    \"\"\"\n",
+    "    Truncate the value in pi into po.\n",
+    "\n",
+    "    Args:\n",
+    "        pi: PortWordLength: input port word length\n",
+    "        po: PortWordLength: input port word length\n",
+    "    \"\"\"\n",
     "    # separate the bits into two parts, before binary point and after binary point\n",
-    "    bits_before_point = pi.value[:pi.p]\n",
-    "    bits_after_point = pi.value[pi.p:]\n",
-    "    \n",
-    "    # truncate or appending 0 \n",
+    "    bits_str: str = str(pi.value)\n",
+    "    bits_before_point = bits_str[: pi.p]\n",
+    "    bits_after_point = bits_str[pi.p :]\n",
+    "\n",
+    "    # truncate or appending 0\n",
     "    if po.p >= pi.p:\n",
     "        bits_before_point = (\"0\" * (po.p - pi.p)) + bits_before_point\n",
     "    else:\n",
-    "        bits_before_point = bits_before_point[pi.p - po.p:]\n",
+    "        bits_before_point = bits_before_point[pi.p - po.p :]\n",
     "\n",
     "    if po.n - po.p >= pi.n - pi.p:\n",
     "        bits_after_point = bits_after_point + \"0\" * (po.n - po.p - pi.n + pi.p)\n",
     "    else:\n",
-    "        bits_after_point = bits_after_point[:(po.n - po.p)]\n",
+    "        bits_after_point = bits_after_point[: (po.n - po.p)]\n",
     "\n",
     "    # combine the both parts\n",
     "    ret = bits_before_point + bits_after_point\n",
-    "    po.set_value(value = ret)\n",
-    "    return po.value"
+    "    po.value = ret"
    ]
   },
   {
@@ -222,17 +246,22 @@
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=2, name=\"p1\", value = \"1111111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
+    "def example_truncation_error_same_position() -> None:\n",
+    "    \"\"\"The example showing truncation error for same number of integer bits\"\"\"\n",
+    "    p1 = PortWordLength(n=7, p=2, name=\"p1\", value=\"1111111\")\n",
+    "    p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
     "\n",
-    "print(\"Truncation Error: \")\n",
-    "print(truncation_error_same_position(p1, p2))\n",
-    "print(\"Example Value to produce the error\")\n",
+    "    print(\"Truncation Error: \")\n",
+    "    print(truncation_error_same_position(p1, p2))\n",
+    "    print(\"Example Value to produce the error\")\n",
     "\n",
-    "truncate(p1, p2)\n",
-    "print(f\"p1 = {p1.value_num()}, bits: {p1.value}\")\n",
-    "print(f\"p2 = {p2.value_num()}, bits: {p2.value}\")\n",
-    "print(f\"p1 - p2 = \", p1.value_num() - p2.value_num())"
+    "    truncate(p1, p2)\n",
+    "    print(f\"p1 = {p1.value_num()}, bits: {p1.value}\")\n",
+    "    print(f\"p2 = {p2.value_num()}, bits: {p2.value}\")\n",
+    "    print(\"p1 - p2 = \", p1.value_num() - p2.value_num())\n",
+    "\n",
+    "\n",
+    "example_truncation_error_same_position()"
    ]
   },
   {
@@ -272,29 +301,59 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def truncation_error(pi, po):\n",
+    "def truncation_error(pi: PortWordLength, po: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Truncation error for the case where input ports and output ports have different number of integer bits.\n",
+    "\n",
+    "    Args:\n",
+    "        pi: PortWordLength: input port word length\n",
+    "        po: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The maximum truncation error if a number is truncated from wordlength pi to wordlength po\n",
+    "    \"\"\"\n",
     "    # remove uneccesary most significant bits\n",
     "    # Remider: The assumption must hold!!\n",
     "    pi_adjusted = PortWordLength(n=pi.n - pi.p + po.p, p=po.p)\n",
     "    # return the truncation error as in case 1\n",
     "    return truncation_error_same_position(pi=pi_adjusted, po=po)\n",
     "\n",
-    "def get_assumption_value(pi, po):\n",
+    "\n",
+    "def get_assumption_value(pi: PortWordLength, po: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Compute the assumption required for truncation to ensure no loss of integer values.\n",
+    "\n",
+    "    Args:\n",
+    "        pi: PortWordLength: input port word length\n",
+    "        po: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The maximum bound pi such that it won't loss integer bits after truncation\n",
+    "    \"\"\"\n",
     "    if pi.p > po.p:\n",
-    "        return 2 ** po.p\n",
-    "    else:\n",
-    "        return float(\"inf\") # no additional assumption needed\n",
+    "        return float(2**po.p)\n",
+    "    return float(\"inf\")  # no additional assumption needed\n",
+    "\n",
     "\n",
-    "def check_assumption_value(pi, po):\n",
+    "def check_assumption_value(pi: PortWordLength, po: PortWordLength) -> bool:\n",
+    "    \"\"\"\n",
+    "    Check if the value of the input port satisfy the assumption for truncation.\n",
+    "\n",
+    "    Args:\n",
+    "        pi: PortWordLength: input port word length\n",
+    "        po: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        If the assumption to prevent integer bit loss is satisfied\n",
+    "    \"\"\"\n",
     "    assumption_value = get_assumption_value(pi, po)\n",
     "    print(f\"Assumption: {pi.name} < {assumption_value}\")\n",
-    "    if not pi.value_num() < assumption_value:\n",
+    "    if pi.value_num() >= assumption_value:\n",
     "        print(\"Assumption failed, truncation of MSB occurs\")\n",
     "        return False\n",
-    "    else:\n",
-    "        print(f\"Assumption Satisfied ({pi.name} = {pi.value_num()} < {assumption_value})\")\n",
-    "        return True\n",
-    "        \n"
+    "\n",
+    "    print(f\"Assumption Satisfied ({pi.name} = {pi.value_num()} < {assumption_value})\")\n",
+    "    return True"
    ]
   },
   {
@@ -307,8 +366,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Assumption: p1 < 4\n",
-      "Assumption Satisfied (p1 = 2.4375 < 4)\n",
+      "Assumption: p1 < 4.0\n",
+      "Assumption Satisfied (p1 = 2.4375 < 4.0)\n",
       "Truncation Error: \n",
       "0.0625\n",
       "Example Value to produce the error\n",
@@ -319,19 +378,24 @@
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
+    "def example_truncation_error() -> None:\n",
+    "    \"\"\"Example showing the truncation error\"\"\"\n",
+    "    p1 = PortWordLength(n=7, p=3, name=\"p1\", value=\"0100111\")\n",
+    "    p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
+    "\n",
+    "    assert check_assumption_value(pi=p1, po=p2)\n",
+    "\n",
+    "    print(\"Truncation Error: \")\n",
+    "    print(truncation_error(p1, p2))\n",
+    "    print(\"Example Value to produce the error\")\n",
     "\n",
-    "assert(check_assumption_value(pi=p1, po=p2))\n",
+    "    truncate(p1, p2)\n",
+    "    print(f\"p1 = {p1.value_num()}, bits: {p1.value}\")\n",
+    "    print(f\"p2 = {p2.value_num()}, bits: {p2.value}\")\n",
+    "    print(\"p1 - p2 = \", p1.value_num() - p2.value_num())\n",
     "\n",
-    "print(\"Truncation Error: \")\n",
-    "print(truncation_error(p1, p2))\n",
-    "print(\"Example Value to produce the error\")\n",
     "\n",
-    "truncate(p1, p2)\n",
-    "print(f\"p1 = {p1.value_num()}, bits: {p1.value}\")\n",
-    "print(f\"p2 = {p2.value_num()}, bits: {p2.value}\")\n",
-    "print(f\"p1 - p2 = \", p1.value_num() - p2.value_num())"
+    "example_truncation_error()"
    ]
   },
   {
@@ -374,22 +438,46 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def compute_required_word_length_add(in1: PortWordLength, in2:PortWordLength) -> PortWordLength:\n",
+    "def compute_required_word_length_add(in1: PortWordLength, in2: PortWordLength) -> PortWordLength:\n",
+    "    \"\"\"\n",
+    "    Compute the minimum word length to hold all values after addition operation on the inputs without lossing any information.\n",
+    "\n",
+    "    Args:\n",
+    "        in1: PortWordLength: input port word length\n",
+    "        in2: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The minimum word length to hold all values after addition operation on the inputs without lossing any information.\n",
+    "    \"\"\"\n",
     "    new_n = max(in1.n, in2.n - in2.p + in1.p) - min(0, in1.p - in2.p) + 1\n",
     "    new_p = max(in1.p, in2.p) + 1\n",
     "    return PortWordLength(n=new_n, p=new_p)\n",
     "\n",
-    "def error_truncation_add(p1, p2, po):\n",
-    "    print(f\"Input {p1.name}: (n={p1.n}, p={p1.p})\")\n",
-    "    print(f\"Input {p2.name}: (n={p2.n}, p={p2.p})\")\n",
-    "    p_ideal = compute_required_word_length_add(in1=p1, in2=p2)\n",
+    "\n",
+    "def error_truncation_add(in1: PortWordLength, in2: PortWordLength, out: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Compute the maximum error of truncation caused by the addition operation.\n",
+    "\n",
+    "    The addition operation is perform for ports out = in1 + in2.\n",
+    "    The value of in1 + in2 is truncated to fit in out\n",
+    "\n",
+    "    Args:\n",
+    "        in1: PortWordLength: input port word length\n",
+    "        in2: PortWordLength: input port word length\n",
+    "        out: PortWordLength: output port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The maximum error of truncation caused by the addition operation\n",
+    "    \"\"\"\n",
+    "    print(f\"Input {in1.name}: (n={in1.n}, p={in1.p})\")\n",
+    "    print(f\"Input {in2.name}: (n={in2.n}, p={in2.p})\")\n",
+    "    p_ideal = compute_required_word_length_add(in1=in1, in2=in2)\n",
     "    print(f\"Ideal Output: (n={p_ideal.n}, p={p_ideal.p})\")\n",
-    "    print(f\"Actual Output ({po.name}): (n={po.n}, p={po.p})\")\n",
+    "    print(f\"Actual Output ({out.name}): (n={out.n}, p={out.p})\")\n",
     "\n",
-    "    assumption_value = get_assumption_value(pi=p_ideal, po=po)\n",
-    "    print(f\"Assumption: {p1.name} + {p2.name} < {assumption_value}\")\n",
-    "    return truncation_error(pi=p_ideal, po=po)\n",
-    "    "
+    "    assumption_value = get_assumption_value(pi=p_ideal, po=out)\n",
+    "    print(f\"Assumption: {in1.name} + {in2.name} < {assumption_value}\")\n",
+    "    return truncation_error(pi=p_ideal, po=out)"
    ]
   },
   {
@@ -406,19 +494,24 @@
       "Input p2: (n=5, p=2)\n",
       "Ideal Output: (n=8, p=4)\n",
       "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 + p2 < 8\n",
+      "Assumption: p1 + p2 < 8.0\n",
       "Error of the addition calculation: 0.1875\n"
      ]
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
+    "def example_truncation_error_add() -> None:\n",
+    "    \"\"\"Example showing truncation error of addition operation\"\"\"\n",
+    "    p1 = PortWordLength(n=7, p=3, name=\"p1\", value=\"0100111\")\n",
+    "    p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
     "\n",
-    "err = error_truncation_add(p1=p1, p2=p2, po=p3)\n",
+    "    err = error_truncation_add(in1=p1, in2=p2, out=p3)\n",
     "\n",
-    "print(f\"Error of the addition calculation: {err}\")"
+    "    print(f\"Error of the addition calculation: {err}\")\n",
+    "\n",
+    "\n",
+    "example_truncation_error_add()"
    ]
   },
   {
@@ -442,21 +535,46 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def compute_required_word_length_mult(in1: PortWordLength, in2:PortWordLength) -> PortWordLength:\n",
+    "def compute_required_word_length_mult(in1: PortWordLength, in2: PortWordLength) -> PortWordLength:\n",
+    "    \"\"\"\n",
+    "    Compute the minimum word length to hold all values after multiplication operation on the inputs without lossing any information.\n",
+    "\n",
+    "    Args:\n",
+    "        in1: PortWordLength: input port word length\n",
+    "        in2: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The minimum word length to hold all values after multiplication operation on the inputs without lossing any information.\n",
+    "    \"\"\"\n",
     "    new_n = in1.n + in2.n\n",
     "    new_p = in1.p + in2.p\n",
     "    return PortWordLength(n=new_n, p=new_p)\n",
     "\n",
-    "def error_truncation_mult(p1, p2, po):\n",
-    "    print(f\"Input {p1.name}: (n={p1.n}, p={p1.p})\")\n",
-    "    print(f\"Input {p2.name}: (n={p2.n}, p={p2.p})\")\n",
-    "    p_ideal = compute_required_word_length_mult(in1=p1, in2=p2)\n",
+    "\n",
+    "def error_truncation_mult(in1: PortWordLength, in2: PortWordLength, out: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Compute the maximum error of truncation caused by the multiplication operation.\n",
+    "\n",
+    "    The multiplication operation is perform for ports out = in1 * in2.\n",
+    "    The value of in1 * in2 is truncated to fit in out\n",
+    "\n",
+    "    Args:\n",
+    "        in1: PortWordLength: input port word length\n",
+    "        in2: PortWordLength: input port word length\n",
+    "        out: PortWordLength: output port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The maximum error of truncation caused by the multiplication operation\n",
+    "    \"\"\"\n",
+    "    print(f\"Input {in1.name}: (n={in1.n}, p={in1.p})\")\n",
+    "    print(f\"Input {in2.name}: (n={in2.n}, p={in2.p})\")\n",
+    "    p_ideal = compute_required_word_length_mult(in1=in1, in2=in2)\n",
     "    print(f\"Ideal Output: (n={p_ideal.n}, p={p_ideal.p})\")\n",
-    "    print(f\"Actual Output ({po.name}): (n={po.n}, p={po.p})\")\n",
+    "    print(f\"Actual Output ({out.name}): (n={out.n}, p={out.p})\")\n",
     "\n",
-    "    assumption_value = get_assumption_value(pi=p_ideal, po=po)\n",
-    "    print(f\"Assumption: {p1.name} * {p2.name} < {assumption_value}\")\n",
-    "    return truncation_error(pi=p_ideal, po=po)"
+    "    assumption_value = get_assumption_value(pi=p_ideal, po=out)\n",
+    "    print(f\"Assumption: {in1.name} * {in2.name} < {assumption_value}\")\n",
+    "    return truncation_error(pi=p_ideal, po=out)"
    ]
   },
   {
@@ -473,19 +591,24 @@
       "Input p2: (n=5, p=2)\n",
       "Ideal Output: (n=12, p=5)\n",
       "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 * p2 < 8\n",
+      "Assumption: p1 * p2 < 8.0\n",
       "Error of the multiplication calculation: 0.2421875\n"
      ]
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
+    "def example_truncation_error_mult() -> None:\n",
+    "    \"\"\"Example showing truncation error of multiplication operation\"\"\"\n",
+    "    p1 = PortWordLength(n=7, p=3, name=\"p1\", value=\"0100111\")\n",
+    "    p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
+    "\n",
+    "    err = error_truncation_mult(in1=p1, in2=p2, out=p3)\n",
     "\n",
-    "err = error_truncation_mult(p1=p1, p2=p2, po=p3)\n",
+    "    print(f\"Error of the multiplication calculation: {err}\")\n",
     "\n",
-    "print(f\"Error of the multiplication calculation: {err}\")"
+    "\n",
+    "example_truncation_error_mult()"
    ]
   },
   {
@@ -541,8 +664,19 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def error_propagation_add(p1, p2, po):\n",
-    "    return p1.e + p2.e"
+    "def error_propagation_add(in1: PortWordLength, in2: PortWordLength, out: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Compute the propagation error caused by addtion operation.\n",
+    "\n",
+    "    Args:\n",
+    "        in1: PortWordLength: input port word length\n",
+    "        in2: PortWordLength: input port word length\n",
+    "        out: PortWordLength: output port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The propagation error caused by addtion operation given the known error in the inputs.\n",
+    "    \"\"\"\n",
+    "    return float(in1.e + in2.e)"
    ]
   },
   {
@@ -559,7 +693,7 @@
       "Input p2: (n=5, p=2)\n",
       "Ideal Output: (n=8, p=4)\n",
       "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 + p2 < 8\n",
+      "Assumption: p1 + p2 < 8.0\n",
       "Propagation error: 0.7\n",
       "Truncation error: 0.1875\n",
       "Error bound of the addition calculation: 0.8875\n"
@@ -567,17 +701,24 @@
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=3, e=0.5, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, e=0.2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
+    "def example_propagation_error_add() -> None:\n",
+    "    \"\"\"Example showing propagation error of addition operation\"\"\"\n",
+    "    e1 = 0.5\n",
+    "    e2 = 0.2\n",
+    "    p1 = PortWordLength(n=7, p=3, e=e1, name=\"p1\", value=\"0100111\")\n",
+    "    p2 = PortWordLength(n=5, p=2, e=e2, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
+    "\n",
+    "    e_p = error_propagation_add(in1=p1, in2=p2, out=p3)\n",
+    "    e_t = error_truncation_add(in1=p1, in2=p2, out=p3)\n",
+    "\n",
+    "    err = e_p + e_t\n",
+    "    print(f\"Propagation error: {e_p}\")\n",
+    "    print(f\"Truncation error: {e_t}\")\n",
+    "    print(f\"Error bound of the addition calculation: {err}\")\n",
     "\n",
-    "e_p = error_propagation_add(p1=p1, p2=p2, po=p3)\n",
-    "e_t = error_truncation_add(p1=p1, p2=p2, po=p3)\n",
     "\n",
-    "err = e_p + e_t\n",
-    "print(f\"Propagation error: {e_p}\")\n",
-    "print(f\"Truncation error: {e_t}\")\n",
-    "print(f\"Error bound of the addition calculation: {err}\")"
+    "example_propagation_error_add()"
    ]
   },
   {
@@ -600,11 +741,19 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def get_actual_possible_value(in_port: PortWordLength):\n",
-    "    return (1 - (2 ** -in_port.n) )* 2 ** in_port.p\n",
-    "\n",
-    "def error_propagation_mult(p1, p2, po):\n",
-    "    return p1.a * p2.e + p2.a * p1.e + p1.e * p2.e"
+    "def error_propagation_mult(in1: PortWordLength, in2: PortWordLength, out: PortWordLength) -> float:\n",
+    "    \"\"\"\n",
+    "    Compute the propagation error caused by multiplication operation.\n",
+    "\n",
+    "    Args:\n",
+    "        in1: PortWordLength: input port word length\n",
+    "        in2: PortWordLength: input port word length\n",
+    "        out: PortWordLength: output port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The propagation error caused by multiplication operation given the known error in the inputs.\n",
+    "    \"\"\"\n",
+    "    return float(in1.a * in2.e + in2.a * in1.e + in1.e * in2.e)"
    ]
   },
   {
@@ -621,7 +770,7 @@
       "Input p2: (n=5, p=2)\n",
       "Ideal Output: (n=12, p=5)\n",
       "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 * p2 < 8\n",
+      "Assumption: p1 * p2 < 8.0\n",
       "Propagation error: 2.81875\n",
       "Truncation error: 0.2421875\n",
       "Error bound of the multiplication calculation: 3.0609375\n"
@@ -629,17 +778,24 @@
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=3, e=0.5, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, e=0.3, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
+    "def example_propagation_error_mult() -> None:\n",
+    "    \"\"\"Example showing propagation error of multiplication operation\"\"\"\n",
+    "    e1 = 0.5\n",
+    "    e2 = 0.3\n",
+    "    p1 = PortWordLength(n=7, p=3, e=e1, name=\"p1\", value=\"0100111\")\n",
+    "    p2 = PortWordLength(n=5, p=2, e=e2, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
+    "\n",
+    "    e_p = error_propagation_mult(in1=p1, in2=p2, out=p3)\n",
+    "    e_t = error_truncation_mult(in1=p1, in2=p2, out=p3)\n",
+    "\n",
+    "    err = e_p + e_t\n",
+    "    print(f\"Propagation error: {e_p}\")\n",
+    "    print(f\"Truncation error: {e_t}\")\n",
+    "    print(f\"Error bound of the multiplication calculation: {err}\")\n",
     "\n",
-    "e_p = error_propagation_mult(p1=p1, p2=p2, po=p3)\n",
-    "e_t = error_truncation_mult(p1=p1, p2=p2, po=p3)\n",
     "\n",
-    "err = e_p + e_t\n",
-    "print(f\"Propagation error: {e_p}\")\n",
-    "print(f\"Truncation error: {e_t}\")\n",
-    "print(f\"Error bound of the multiplication calculation: {err}\")"
+    "example_propagation_error_mult()"
    ]
   },
   {
@@ -683,63 +839,106 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def form_contract_add(in_port1, in_port2, out_port):\n",
-    "    ret_contract = {}\n",
+    "def form_contract_add(\n",
+    "    in_port1: PortWordLength, in_port2: PortWordLength, out_port: PortWordLength\n",
+    ") -> dict[str, object]:\n",
+    "    \"\"\"\n",
+    "    Create contract in dictionary form for an addition operation.\n",
+    "\n",
+    "    The contract includes information about the bound for its output value and the bound for its maximum error\n",
+    "\n",
+    "    Args:\n",
+    "        in_port1: PortWordLength: input port word length\n",
+    "        in_port2: PortWordLength: input port word length\n",
+    "        out_port: PortWordLength: output port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The contract in dictionary form for an addition operation.\n",
+    "    \"\"\"\n",
+    "    ret_contract: dict[str, object] = {}\n",
     "    # define input/output vars\n",
-    "    ret_contract[\"input_vars\"] = [f\"{in_port1.name}_a\", f\"{in_port1.name}_e\",\n",
-    "                                 f\"{in_port2.name}_a\", f\"{in_port2.name}_e\"]\n",
+    "    ret_contract[\"input_vars\"] = [\n",
+    "        f\"{in_port1.name}_a\",\n",
+    "        f\"{in_port1.name}_e\",\n",
+    "        f\"{in_port2.name}_a\",\n",
+    "        f\"{in_port2.name}_e\",\n",
+    "    ]\n",
     "    ret_contract[\"output_vars\"] = [f\"{out_port.name}_a\", f\"{out_port.name}_e\"]\n",
     "    # get assumption\n",
     "    ideal_out_port = compute_required_word_length_add(in1=in_port1, in2=in_port2)\n",
     "    assumption_value = get_assumption_value(pi=ideal_out_port, po=out_port)\n",
     "    # write assumption in the contract\n",
-    "    if assumption_value != float(\"inf\"):\n",
-    "        ret_contract[\"assumptions\"] =  [{\"coefficients\":{f\"{in_port1.name}_a\":1, f\"{in_port2.name}_a\":1},\n",
-    "                                    \"constant\":assumption_value}]\n",
-    "    else:\n",
+    "    if assumption_value == float(\"inf\"):\n",
     "        ret_contract[\"assumptions\"] = []\n",
+    "    else:\n",
+    "        ret_contract[\"assumptions\"] = [\n",
+    "            {\"coefficients\": {f\"{in_port1.name}_a\": 1, f\"{in_port2.name}_a\": 1}, \"constant\": assumption_value}\n",
+    "        ]\n",
     "    # get guarantee\n",
-    "    e_t = truncation_error(pi=ideal_out_port, po=out_port)\n",
+    "    e_trunc = truncation_error(pi=ideal_out_port, po=out_port)\n",
     "\n",
     "    # write guarantee in the contract, note the propagation is encoded in the polyhedral constraints\n",
-    "    ret_contract[\"guarantees\"] =  [{\"coefficients\":{f\"{in_port1.name}_e\":-1, f\"{in_port2.name}_e\":-1, f\"{out_port.name}_e\": 1},\n",
-    "                                \"constant\":e_t},\n",
-    "                                {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":out_port.a},\n",
-    "                                #{\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":in_port1.a + in_port2.a},\n",
-    "                                {\"coefficients\":{f\"{out_port.name}_a\": 1, f\"{in_port1.name}_a\": -1, f\"{in_port2.name}_a\": -1}, \"constant\":0}\n",
-    "                                ]\n",
+    "    ret_contract[\"guarantees\"] = [\n",
+    "        {\n",
+    "            \"coefficients\": {f\"{in_port1.name}_e\": -1, f\"{in_port2.name}_e\": -1, f\"{out_port.name}_e\": 1},\n",
+    "            \"constant\": e_trunc,\n",
+    "        },\n",
+    "        {\"coefficients\": {f\"{out_port.name}_a\": 1}, \"constant\": out_port.a},\n",
+    "        # {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":in_port1.a + in_port2.a},\n",
+    "        {\"coefficients\": {f\"{out_port.name}_a\": 1, f\"{in_port1.name}_a\": -1, f\"{in_port2.name}_a\": -1}, \"constant\": 0},\n",
+    "    ]\n",
     "    return ret_contract\n",
     "\n",
     "\n",
-    "def form_contract_mult_const(in_port1, in_port_const, out_port):\n",
-    "    ret_contract = {}\n",
+    "def form_contract_mult_const(\n",
+    "    in_port: PortWordLength, in_port_const: PortWordLength, out_port: PortWordLength\n",
+    ") -> dict[str, object]:\n",
+    "    \"\"\"\n",
+    "    Create contract in dictionary form for a constant coefficient multiplication operation.\n",
+    "\n",
+    "    The contract includes information about the bound for its output value and the bound for its maximum error\n",
+    "\n",
+    "    Args:\n",
+    "        in_port: PortWordLength: input port word length\n",
+    "        in_port_const: PortWordLength: constant input port word length\n",
+    "        out_port: PortWordLength: output port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The contract in dictionary form for a constant coefficient multiplication operation.\n",
+    "    \"\"\"\n",
+    "    ret_contract: dict[str, object] = {}\n",
     "    # define input/output vars\n",
-    "    ret_contract[\"input_vars\"] = [f\"{in_port1.name}_a\", f\"{in_port1.name}_e\"]\n",
+    "    ret_contract[\"input_vars\"] = [f\"{in_port.name}_a\", f\"{in_port.name}_e\"]\n",
     "    ret_contract[\"output_vars\"] = [f\"{out_port.name}_a\", f\"{out_port.name}_e\"]\n",
     "    # get assumption\n",
-    "    ideal_out_port = compute_required_word_length_mult(in1=in_port1, in2=in_port_const)\n",
+    "    ideal_out_port = compute_required_word_length_mult(in1=in_port, in2=in_port_const)\n",
     "    assumption_value = get_assumption_value(pi=ideal_out_port, po=out_port)\n",
     "    # write assumption in the contract\n",
-    "    if assumption_value != float(\"inf\"):\n",
-    "        print(assumption_value)\n",
-    "        ret_contract[\"assumptions\"] =  [{\"coefficients\":{f\"{in_port1.name}_a\": in_port_const.a},\n",
-    "                                    \"constant\":assumption_value}]\n",
-    "    else:\n",
+    "    if assumption_value == float(\"inf\"):\n",
     "        ret_contract[\"assumptions\"] = []\n",
+    "    else:\n",
+    "        print(assumption_value)\n",
+    "        ret_contract[\"assumptions\"] = [\n",
+    "            {\"coefficients\": {f\"{in_port.name}_a\": in_port_const.a}, \"constant\": assumption_value}\n",
+    "        ]\n",
     "    # get guarantee\n",
     "    print(ideal_out_port.to_string())\n",
-    "    e_t = truncation_error(pi=ideal_out_port, po=out_port)\n",
+    "    e_trunc = truncation_error(pi=ideal_out_port, po=out_port)\n",
     "\n",
     "    # write guarantee in the contract, note the propagation is encoded in the polyhedral constraints\n",
-    "    ret_contract[\"guarantees\"] =  [{\"coefficients\":\n",
-    "                                    {   f\"{in_port1.name}_e\":-in_port_const.a+in_port_const.e, \n",
-    "                                        f\"{in_port1.name}_a\":-in_port_const.e,\n",
-    "                                        f\"{out_port.name}_e\": 1},\n",
-    "                                    \"constant\":e_t},\n",
-    "                                    {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":out_port.a},\n",
-    "                                    #{\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":in_port_const.a * in_port1.a},\n",
-    "                                    {\"coefficients\":{f\"{out_port.name}_a\": 1, f\"{in_port1.name}_a\": -in_port_const.a}, \"constant\":0}\n",
-    "                                    ]\n",
+    "    ret_contract[\"guarantees\"] = [\n",
+    "        {\n",
+    "            \"coefficients\": {\n",
+    "                f\"{in_port.name}_e\": -in_port_const.a + in_port_const.e,\n",
+    "                f\"{in_port.name}_a\": -in_port_const.e,\n",
+    "                f\"{out_port.name}_e\": 1,\n",
+    "            },\n",
+    "            \"constant\": e_trunc,\n",
+    "        },\n",
+    "        {\"coefficients\": {f\"{out_port.name}_a\": 1}, \"constant\": out_port.a},\n",
+    "        # {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":in_port_const.a * in_port.a},\n",
+    "        {\"coefficients\": {f\"{out_port.name}_a\": 1, f\"{in_port.name}_a\": -in_port_const.a}, \"constant\": 0},\n",
+    "    ]\n",
     "    return ret_contract"
    ]
   },
@@ -753,7 +952,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "{'input_vars': ['p1_a', 'p1_e', 'p2_a', 'p2_e'], 'output_vars': ['p3_a', 'p3_e'], 'assumptions': [{'coefficients': {'p1_a': 1, 'p2_a': 1}, 'constant': 8}], 'guarantees': [{'coefficients': {'p1_e': -1, 'p2_e': -1, 'p3_e': 1}, 'constant': 0.1875}, {'coefficients': {'p3_a': 1}, 'constant': 7.75}, {'coefficients': {'p3_a': 1, 'p1_a': -1, 'p2_a': -1}, 'constant': 0}]}\n",
+      "{'input_vars': ['p1_a', 'p1_e', 'p2_a', 'p2_e'], 'output_vars': ['p3_a', 'p3_e'], 'assumptions': [{'coefficients': {'p1_a': 1, 'p2_a': 1}, 'constant': 8.0}], 'guarantees': [{'coefficients': {'p1_e': -1, 'p2_e': -1, 'p3_e': 1}, 'constant': 0.1875}, {'coefficients': {'p3_a': 1}, 'constant': 7.75}, {'coefficients': {'p3_a': 1, 'p1_a': -1, 'p2_a': -1}, 'constant': 0}]}\n",
       "InVars: [p1_a, p1_e, p2_a, p2_e]\n",
       "OutVars:[p3_a, p3_e]\n",
       "A: [\n",
@@ -764,7 +963,7 @@
       "  p3_a <= 7.75\n",
       "  -p1_a - p2_a + p3_a <= 0.0\n",
       "]\n",
-      "8\n",
+      "8.0\n",
       "Port: , (n, p) = (12, 5), e = 0, a = 31.9921875\n",
       "InVars: [p4_a, p4_e]\n",
       "OutVars:[p6_a, p6_e]\n",
@@ -780,21 +979,26 @@
     }
    ],
    "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
-    "c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
-    "print(c1)\n",
-    "contract1 = PolyhedralContract.from_dict(c1)\n",
-    "print(str(contract1))\n",
-    "\n",
-    "\n",
-    "p4 = PortWordLength(n=7, p=3, name=\"p4\")\n",
-    "p5 = PortWordLength(n=5, p=2, e=0.03, value=\"11011\", name=\"p5\") # const\n",
-    "p6 = PortWordLength(n=5, p=3, name=\"p6\")\n",
-    "c2 = form_contract_mult_const(in_port1=p4, in_port_const=p5, out_port=p6)\n",
-    "contract2 = PolyhedralContract.from_dict(c2)\n",
-    "print(str(contract2))"
+    "def example_contract_formulation() -> None:\n",
+    "    \"\"\"Example of contract formulation\"\"\"\n",
+    "    p1 = PortWordLength(n=7, p=3, name=\"p1\")\n",
+    "    p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
+    "    c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
+    "    print(c1)\n",
+    "    contract1 = PolyhedralContract.from_dict(c1)\n",
+    "    print(str(contract1))\n",
+    "\n",
+    "    e5 = 0.03\n",
+    "    p4 = PortWordLength(n=7, p=3, name=\"p4\")\n",
+    "    p5 = PortWordLength(n=5, p=2, e=e5, value=\"11011\", name=\"p5\")  # const\n",
+    "    p6 = PortWordLength(n=5, p=3, name=\"p6\")\n",
+    "    c2 = form_contract_mult_const(in_port=p4, in_port_const=p5, out_port=p6)\n",
+    "    contract2 = PolyhedralContract.from_dict(c2)\n",
+    "    print(str(contract2))\n",
+    "\n",
+    "\n",
+    "example_contract_formulation()"
    ]
   },
   {
@@ -806,7 +1010,7 @@
     "#### Example 1\n",
     "Consider the following simple system with two adders.\n",
     "\n",
-    "![Example 1](./figures/example1.png)\n",
+    "![Example 1](https://github.com/FormalSystems/media/blob/main/case_studies/digital_signal_processing/example1.png?raw=True)\n",
     "\n",
     "The word length is labeled beside the variable. \n",
     "We will show how Pacti can be applied to get the error bound for the word length."
@@ -826,54 +1030,29 @@
    "execution_count": 17,
    "id": "690cd39b",
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Contract 1:\n",
-      "InVars: [p1_a, p1_e, p2_a, p2_e]\n",
-      "OutVars:[p3_a, p3_e]\n",
-      "A: [\n",
-      "  p1_a + p2_a <= 8.0\n",
-      "]\n",
-      "G: [\n",
-      "  -p1_e - p2_e + p3_e <= 0.125\n",
-      "  p3_a <= 7.75\n",
-      "  -p1_a - p2_a + p3_a <= 0.0\n",
-      "]\n",
-      "Contract 2:\n",
-      "InVars: [p3_a, p3_e, p4_a, p4_e]\n",
-      "OutVars:[p5_a, p5_e]\n",
-      "A: [\n",
-      "  p3_a + p4_a <= 8.0\n",
-      "]\n",
-      "G: [\n",
-      "  -p3_e - p4_e + p5_e <= 0.0625\n",
-      "  p5_a <= 7.875\n",
-      "  -p3_a - p4_a + p5_a <= 0.0\n",
-      "]\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
-    "def create_example1():\n",
-    "    p1 = PortWordLength(n=5, p=2, name = \"p1\")\n",
-    "    p2 = PortWordLength(n=5, p=3, name = \"p2\")\n",
-    "    p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
+    "def create_example1() -> tuple:\n",
+    "    \"\"\"\n",
+    "    Generate the example contracts and ports for the example\n",
+    "\n",
+    "    Returns:\n",
+    "        A tuple with 2 elements. The first one is the list of contract, and the other is the list of ports\n",
+    "    \"\"\"\n",
+    "    p1 = PortWordLength(n=5, p=2, name=\"p1\")\n",
+    "    p2 = PortWordLength(n=5, p=3, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
     "    c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
     "\n",
-    "    p4 = PortWordLength(n=7, p=3, name = \"p4\")\n",
-    "    p5 = PortWordLength(n=6, p=3, name = \"p5\")\n",
+    "    p4 = PortWordLength(n=7, p=3, name=\"p4\")\n",
+    "    p5 = PortWordLength(n=6, p=3, name=\"p5\")\n",
     "    c2 = form_contract_add(in_port1=p3, in_port2=p4, out_port=p5)\n",
     "\n",
     "    contract1 = PolyhedralContract.from_dict(c1)\n",
     "    contract2 = PolyhedralContract.from_dict(c2)\n",
-    "    print(\"Contract 1:\\n\" + str(contract1))\n",
-    "    print(\"Contract 2:\\n\" + str(contract2))\n",
-    "    return contract1, contract2, p1, p2, p3, p4, p5\n",
-    "    \n",
-    "contract1, contract2, p1, p2, p3, p4, p5 = create_example1()"
+    "    contracts = [contract1, contract2]\n",
+    "    ports = [p1, p2, p3, p4, p5]\n",
+    "    return contracts, ports"
    ]
   },
   {
@@ -895,6 +1074,28 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
+      "Contract 1:\n",
+      "InVars: [p1_a, p1_e, p2_a, p2_e]\n",
+      "OutVars:[p3_a, p3_e]\n",
+      "A: [\n",
+      "  p1_a + p2_a <= 8.0\n",
+      "]\n",
+      "G: [\n",
+      "  -p1_e - p2_e + p3_e <= 0.125\n",
+      "  p3_a <= 7.75\n",
+      "  -p1_a - p2_a + p3_a <= 0.0\n",
+      "]\n",
+      "Contract 2:\n",
+      "InVars: [p3_a, p3_e, p4_a, p4_e]\n",
+      "OutVars:[p5_a, p5_e]\n",
+      "A: [\n",
+      "  p3_a + p4_a <= 8.0\n",
+      "]\n",
+      "G: [\n",
+      "  -p3_e - p4_e + p5_e <= 0.0625\n",
+      "  p5_a <= 7.875\n",
+      "  -p3_a - p4_a + p5_a <= 0.0\n",
+      "]\n",
       "Contract Sys:\n",
       "InVars: [p1_a, p1_e, p2_a, p2_e, p4_a, p4_e]\n",
       "OutVars:[p5_a, p5_e]\n",
@@ -912,8 +1113,18 @@
     }
    ],
    "source": [
-    "contract_sys = contract1.compose(contract2)\n",
-    "print(\"Contract Sys:\\n\" + str(contract_sys))"
+    "def example_composition_two_adder() -> None:\n",
+    "    \"\"\"Example of showing composition of two adder contracts\"\"\"\n",
+    "    contracts, ports = create_example1()\n",
+    "    contract1 = contracts[0]\n",
+    "    contract2 = contracts[1]\n",
+    "    print(\"Contract 1:\\n\" + str(contract1))\n",
+    "    print(\"Contract 2:\\n\" + str(contract2))\n",
+    "    contract_sys = contract1.compose(contract2)\n",
+    "    print(\"Contract Sys:\\n\" + str(contract_sys))\n",
+    "\n",
+    "\n",
+    "example_composition_two_adder()"
    ]
   },
   {
@@ -935,17 +1146,30 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def form_contract_input(in_port):\n",
-    "    ret_contract = {}\n",
+    "def form_contract_input(in_port: PortWordLength) -> dict[str, object]:\n",
+    "    \"\"\"\n",
+    "    Create contract in dictionary form for a input port to the system.\n",
+    "\n",
+    "    The contract includes information about the bound for its output value and the bound for its maximum error\n",
+    "\n",
+    "    Args:\n",
+    "        in_port: PortWordLength: input port word length\n",
+    "\n",
+    "    Returns:\n",
+    "        The contract in dictionary form for a input port to the system.\n",
+    "    \"\"\"\n",
+    "    ret_contract: dict[str, object] = {}\n",
     "    # define input/output vars\n",
     "    ret_contract[\"input_vars\"] = []\n",
     "    ret_contract[\"output_vars\"] = [f\"{in_port.name}_a\", f\"{in_port.name}_e\"]\n",
     "    # get assumption\n",
     "    ret_contract[\"assumptions\"] = []\n",
-    "    ret_contract[\"guarantees\"] =  [ {\"coefficients\": {f\"{in_port.name}_a\": 1}, \"constant\": in_port.a},\n",
-    "                                    {\"coefficients\": {f\"{in_port.name}_a\": -1}, \"constant\": 0},\n",
-    "                                    {\"coefficients\": {f\"{in_port.name}_e\": 1}, \"constant\": in_port.e},\n",
-    "                                    {\"coefficients\": {f\"{in_port.name}_e\": -1}, \"constant\": -in_port.e}]\n",
+    "    ret_contract[\"guarantees\"] = [\n",
+    "        {\"coefficients\": {f\"{in_port.name}_a\": 1}, \"constant\": in_port.a},\n",
+    "        {\"coefficients\": {f\"{in_port.name}_a\": -1}, \"constant\": 0},\n",
+    "        {\"coefficients\": {f\"{in_port.name}_e\": 1}, \"constant\": in_port.e},\n",
+    "        {\"coefficients\": {f\"{in_port.name}_e\": -1}, \"constant\": -in_port.e},\n",
+    "    ]\n",
     "    return ret_contract"
    ]
   },
@@ -960,24 +1184,41 @@
      "output_type": "stream",
      "text": [
       "Composition Error\n",
-      "The guarantees \n",
+      "Could not eliminate variables ['p2_a']\n",
+      "by refining the assumptions \n",
+      "[\n",
+      "  p2_a <= 4.125\n",
+      "]\n",
+      "using guarantees \n",
       "[\n",
       "  p2_a <= 7.75\n",
       "  -p2_a <= 0.0\n",
       "  p2_e = 0.0\n",
       "]\n",
-      "were insufficient to abduce the assumptions \n",
-      "[\n",
-      "  p4_a <= 0.25\n",
-      "  p2_a <= 4.125\n",
-      "]\n",
-      "by eliminating the variables \n",
-      "[<Var p2_a>, <Var p2_e>, <Var p5_a>, <Var p5_e>]\n"
+      "\n"
      ]
     }
    ],
    "source": [
-    "def test_example_1():\n",
+    "def composition_with_inputs(\n",
+    "    contracts: list[PolyhedralContract], ports: list[PortWordLength]\n",
+    ") -> PolyhedralContract | None:\n",
+    "    \"\"\"\n",
+    "    Composition of the two adder system considering input infomation\n",
+    "\n",
+    "    Args:\n",
+    "        contracts: list of PolyhedralContract for the adders\n",
+    "        ports: list of port wordlengths for the ports\n",
+    "\n",
+    "    Returns:\n",
+    "        The composition result if the composition is valid\n",
+    "    \"\"\"\n",
+    "    p1 = ports[0]\n",
+    "    p2 = ports[1]\n",
+    "    p4 = ports[3]\n",
+    "    contract1 = contracts[0]\n",
+    "    contract2 = contracts[1]\n",
+    "\n",
     "    c_p1 = form_contract_input(in_port=p1)\n",
     "    c_p2 = form_contract_input(in_port=p2)\n",
     "    c_p4 = form_contract_input(in_port=p4)\n",
@@ -985,33 +1226,44 @@
     "    contract_p1 = PolyhedralContract.from_dict(c_p1)\n",
     "    contract_p2 = PolyhedralContract.from_dict(c_p2)\n",
     "    contract_p4 = PolyhedralContract.from_dict(c_p4)\n",
-    "    \n",
-    "    try: \n",
+    "\n",
+    "    # compose the system\n",
+    "    try:\n",
     "        contract_sys = contract1.compose(contract2)\n",
+    "    except ValueError as e:\n",
+    "        print(\"Composition Error\")\n",
+    "        print(e)\n",
+    "        return None\n",
+    "    try:\n",
     "        contract_sys = contract_p1.compose(contract_sys)\n",
+    "    except ValueError as e:\n",
+    "        print(\"Composition Error\")\n",
+    "        print(e)\n",
+    "        return None\n",
+    "    try:\n",
     "        contract_sys = contract_p2.compose(contract_sys)\n",
+    "    except ValueError as e:\n",
+    "        print(\"Composition Error\")\n",
+    "        print(e)\n",
+    "        return None\n",
+    "    try:\n",
     "        contract_sys = contract_p4.compose(contract_sys)\n",
-    "        print(\"Contract Sys:\\n\" + str(contract_sys))\n",
-    "        return contract_sys\n",
     "    except ValueError as e:\n",
     "        print(\"Composition Error\")\n",
     "        print(e)\n",
+    "        return None\n",
+    "\n",
+    "    print(\"Contract Sys:\\n\" + str(contract_sys))\n",
+    "    return contract_sys\n",
     "\n",
-    "contract_sys = test_example_1()"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "67da6d8f",
-   "metadata": {},
-   "source": [
-    "Pacti raises an error about the composition, saying that the guarantees are insufficient to abduce the assumption.\n",
-    "What is wrong with the composition?\n",
     "\n",
-    "The fact is that overflow could occur in the system, and thus the assumption might be violated given the input values.\n",
-    "For example, if $P_2$ is 7.75, and $P_1$ is nonzero, the addition result requires a carry that cannot be expressed in three integer bits.\n",
-    "As a result, the guarantees of the inputs are insufficient to abduce the assumption of the operation contracts."
+    "def example_composition_without_input_bounds() -> None:\n",
+    "    \"\"\"Example of composition without input bounds\"\"\"\n",
+    "    contracts, ports = create_example1()\n",
+    "    composition_with_inputs(contracts, ports)\n",
+    "\n",
+    "\n",
+    "example_composition_without_input_bounds()"
    ]
   },
   {
@@ -1037,6 +1289,7 @@
       "InVars: []\n",
       "OutVars:[p5_a, p5_e]\n",
       "A: [\n",
+      "  \n",
       "]\n",
       "G: [\n",
       "  p5_a <= 5.8125\n",
@@ -1046,10 +1299,19 @@
     }
    ],
    "source": [
-    "p1.set_value(\"10000\")\n",
-    "p2.set_value(\"01111\")\n",
-    "p4.set_value(\"0000001\")\n",
-    "contract_sys = test_example_1()"
+    "def example_composition_with_input_bounds() -> None:\n",
+    "    \"\"\"Example of composition with correct input bounds\"\"\"\n",
+    "    contracts, ports = create_example1()\n",
+    "    p1 = ports[0]\n",
+    "    p2 = ports[1]\n",
+    "    p4 = ports[3]\n",
+    "    p1.value = \"10000\"\n",
+    "    p2.value = \"01111\"\n",
+    "    p4.value = \"0000001\"\n",
+    "    composition_with_inputs(contracts, ports)\n",
+    "\n",
+    "\n",
+    "example_composition_with_input_bounds()"
    ]
   },
   {
@@ -1073,7 +1335,7 @@
     "Quotient is a contract operation that allows us to break down specifications to a certain subsystem.\n",
     "In this example, we try to reduce the word length of $P_3$.\n",
     "\n",
-    "![Example 1](./figures/example1.png)"
+    "![Example 1](https://github.com/FormalSystems/media/blob/main/case_studies/digital_signal_processing/example1.png?raw=True)"
    ]
   },
   {
@@ -1108,6 +1370,7 @@
       "InVars: []\n",
       "OutVars:[p5_a, p5_e]\n",
       "A: [\n",
+      "  \n",
       "]\n",
       "G: [\n",
       "  p5_e <= 0.1\n",
@@ -1116,18 +1379,29 @@
     }
    ],
    "source": [
-    "def get_desired_system_contract():\n",
-    "    ret_contract = {}\n",
+    "def get_desired_system_contract() -> PolyhedralContract:\n",
+    "    \"\"\"\n",
+    "    Generate contracts for the top-level specification\n",
+    "\n",
+    "    Returns:\n",
+    "        The top-level specification contract\n",
+    "    \"\"\"\n",
+    "    ret_contract: dict[str, object] = {}\n",
     "    ret_contract[\"input_vars\"] = []\n",
     "    ret_contract[\"output_vars\"] = [\"p5_a\", \"p5_e\"]\n",
     "    ret_contract[\"assumptions\"] = []\n",
-    "    ret_contract[\"guarantees\"] = [\n",
-    "                                  {\"coefficients\": {f\"p5_e\": 1}, \"constant\": 0.1}]\n",
+    "    ret_contract[\"guarantees\"] = [{\"coefficients\": {\"p5_e\": 1}, \"constant\": 0.1}]\n",
     "    return PolyhedralContract.from_dict(ret_contract)\n",
     "\n",
-    "contract_spec = get_desired_system_contract()\n",
-    "print(\"Top Level Specification:\")\n",
-    "print(str(contract_spec))"
+    "\n",
+    "def example_show_top_level_spec() -> None:\n",
+    "    \"\"\"Example of showing the top level specification\"\"\"\n",
+    "    contract_spec = get_desired_system_contract()\n",
+    "    print(\"Top Level Specification:\")\n",
+    "    print(str(contract_spec))\n",
+    "\n",
+    "\n",
+    "example_show_top_level_spec()"
    ]
   },
   {
@@ -1161,7 +1435,22 @@
     }
    ],
    "source": [
-    "def quotient_example1():\n",
+    "def quotient_example1() -> PolyhedralContract:\n",
+    "    \"\"\"\n",
+    "    Generate quotient based on the top-level specification and the system.\n",
+    "\n",
+    "    Returns:\n",
+    "        The quotient result, the contract for the two adder operations\n",
+    "    \"\"\"\n",
+    "    contract_spec = get_desired_system_contract()\n",
+    "    contracts, ports = create_example1()\n",
+    "    p1 = ports[0]\n",
+    "    p2 = ports[1]\n",
+    "    p4 = ports[3]\n",
+    "    p1.value = \"10000\"\n",
+    "    p2.value = \"01111\"\n",
+    "    p4.value = \"0000001\"\n",
+    "\n",
     "    c_p1 = form_contract_input(in_port=p1)\n",
     "    c_p2 = form_contract_input(in_port=p2)\n",
     "    c_p4 = form_contract_input(in_port=p4)\n",
@@ -1173,12 +1462,17 @@
     "    tmp_c1 = contract_spec.quotient(contract_p1)\n",
     "\n",
     "    tmp_c2 = tmp_c1.quotient(contract_p2)\n",
-    "    tmp_c3 = tmp_c2.quotient(contract_p4)\n",
-    "    return tmp_c3\n",
+    "    return tmp_c2.quotient(contract_p4)\n",
+    "\n",
+    "\n",
+    "def example_quotient() -> None:\n",
+    "    \"\"\"Example of showing using quotient to get contracts for the two adder operations\"\"\"\n",
+    "    quotient_ret = quotient_example1()\n",
+    "    print(\"Quotient of the Spec:\")\n",
+    "    print(str(quotient_ret))\n",
     "\n",
-    "quotient_ret = quotient_example1()\n",
-    "print(\"Quotient of the Spec:\")\n",
-    "print(str(quotient_ret))\n"
+    "\n",
+    "example_quotient()"
    ]
   },
   {
@@ -1209,67 +1503,72 @@
     }
    ],
    "source": [
-    "def create_example1_by_p3(p3_n, p3_p):\n",
-    "    p1 = PortWordLength(n=5, p=2, name = \"p1\")\n",
-    "    p2 = PortWordLength(n=5, p=3, name = \"p2\")\n",
-    "    p3 = PortWordLength(n=p3_n, p=p3_p, name = \"p3\")\n",
+    "def create_example1_by_p3(p3_n: int, p3_p: int) -> tuple:\n",
+    "    \"\"\"\n",
+    "    Generate the example contracts and ports for the example, given different wordlength of p3\n",
+    "\n",
+    "    Args:\n",
+    "        p3_n: the integer which defines the wordlength n for p3\n",
+    "        p3_p: the integer which defines the wordlength p for p3\n",
+    "\n",
+    "    Returns:\n",
+    "        A tuple with 2 elements. The first one is the list of contract, and the other is the list of ports\n",
+    "    \"\"\"\n",
+    "    p1 = PortWordLength(n=5, p=2, name=\"p1\")\n",
+    "    p2 = PortWordLength(n=5, p=3, name=\"p2\")\n",
+    "    p3 = PortWordLength(n=p3_n, p=p3_p, name=\"p3\")\n",
     "    c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
     "\n",
-    "    p4 = PortWordLength(n=7, p=3, name = \"p4\")\n",
-    "    p5 = PortWordLength(n=6, p=3, name = \"p5\")\n",
+    "    p4 = PortWordLength(n=7, p=3, name=\"p4\")\n",
+    "    p5 = PortWordLength(n=6, p=3, name=\"p5\")\n",
     "    c2 = form_contract_add(in_port1=p3, in_port2=p4, out_port=p5)\n",
     "\n",
-    "    p1.set_value(\"10000\")\n",
-    "    p2.set_value(\"01111\")\n",
-    "    p4.set_value(\"0000000\")\n",
+    "    p1.value = \"10000\"\n",
+    "    p2.value = \"01111\"\n",
+    "    p4.value = \"0000000\"\n",
     "\n",
     "    contract1 = PolyhedralContract.from_dict(c1)\n",
     "    contract2 = PolyhedralContract.from_dict(c2)\n",
-    "    #print(\"Contract 1:\\n\" + str(contract1))\n",
-    "    #print(\"Contract 2:\\n\" + str(contract2))\n",
-    "    return contract1, contract2, p1, p2, p3, p4, p5\n",
-    "\n",
-    "def compose_all(contract1, contract2, p1, p2, p3, p4, p5):\n",
-    "    c_p1 = form_contract_input(in_port=p1)\n",
-    "    c_p2 = form_contract_input(in_port=p2)\n",
-    "    c_p4 = form_contract_input(in_port=p4)\n",
+    "    # print(\"Contract 1:\\n\" + str(contract1))\n",
+    "    # print(\"Contract 2:\\n\" + str(contract2))\n",
+    "    contracts = [contract1, contract2]\n",
+    "    ports = [p1, p2, p3, p4, p5]\n",
+    "    return contracts, ports\n",
     "\n",
-    "    contract_p1 = PolyhedralContract.from_dict(c_p1)\n",
-    "    contract_p2 = PolyhedralContract.from_dict(c_p2)\n",
-    "    contract_p4 = PolyhedralContract.from_dict(c_p4)\n",
-    "    \n",
-    "    try: \n",
-    "        contract_sys = contract1.compose(contract2)\n",
-    "        contract_sys = contract_p1.compose(contract_sys)\n",
-    "        contract_sys = contract_p2.compose(contract_sys)\n",
-    "        contract_sys = contract_p4.compose(contract_sys)\n",
-    "        print(\"Contract Sys:\\n\" + str(contract_sys))\n",
-    "        return contract_sys\n",
-    "    except ValueError as e:\n",
-    "        print(\"Composition Error\")\n",
-    "        print(e)\n",
-    "        raise ValueError(\"Composition Fails\")\n",
     "\n",
-    "#contract_sys = test_example_1()\n",
+    "def exhaustive_search() -> int:\n",
+    "    \"\"\"\n",
+    "    Perform exhaustive search on finding the minimum wordlength that satisfy the top-level specification\n",
     "\n",
-    "def exhaustive_search():\n",
+    "    Returns:\n",
+    "        The word length n for p3\n",
+    "    \"\"\"\n",
+    "    quotient_ret = quotient_example1()\n",
     "    for n in range(5, 10):\n",
-    "        #print(n)\n",
-    "        p = 3 # keep the same number of bits (2) before binary point\n",
-    "        contract1, contract2, p1, p2, p3, p4, p5 = create_example1_by_p3(p3_n=n, p3_p=p)\n",
+    "        # print(n)\n",
+    "        p = 3  # keep the same number of bits (2) before binary point\n",
+    "        contracts, ports = create_example1_by_p3(p3_n=n, p3_p=p)\n",
+    "        contract1 = contracts[0]\n",
+    "        contract2 = contracts[1]\n",
     "        try:\n",
     "            contract_sys = contract1.compose(contract2)\n",
-    "            #contract_sys = compose_all(contract1, contract2, p1, p2, p3, p4, p5)\n",
-    "            #print(str(contract_sys))\n",
-    "        except ValueError as e:\n",
+    "            # contract_sys = compose_all(contract1, contract2, p1, p2, p3, p4, p5)\n",
+    "            # print(str(contract_sys))\n",
+    "        except ValueError:\n",
     "            continue\n",
     "\n",
     "        if contract_sys.refines(quotient_ret):\n",
     "            return n\n",
-    "n = exhaustive_search()\n",
-    "print(f\"Wordlength for P3 to satisfy the spec is n = {n}\")\n",
+    "    return -1\n",
+    "\n",
+    "\n",
+    "def example_word_length_optimization() -> None:\n",
+    "    \"\"\"Example of showing word length optimization on p3 for the case study\"\"\"\n",
+    "    n = exhaustive_search()\n",
+    "    print(f\"Wordlength for P3 to satisfy the spec is n = {n}\")\n",
     "\n",
-    "    "
+    "\n",
+    "example_word_length_optimization()"
    ]
   },
   {
@@ -1291,7 +1590,7 @@
     "\n",
     "Now we apply the contract to a simple filter that computes the moving average of the input signal, as shown in the figure below,\n",
     "\n",
-    "![Example2](./figures/digital_filter_flow.png)\n",
+    "![Example2](https://github.com/FormalSystems/media/blob/main/case_studies/digital_signal_processing/digital_filter_flow.png?raw=True)\n",
     "\n",
     ", where the coefficient is $a = 0.2$, $b = 0.6$, and $c = 0.2$.\n",
     "Therefore, $y[n] = 0.2 \\times x[n-2] + 0.6 \\times x[n-1] + 0.2 \\times x[n] $."
@@ -1326,6 +1625,7 @@
       "InVars: []\n",
       "OutVars:[add_out2_a, add_out2_e]\n",
       "A: [\n",
+      "  \n",
       "]\n",
       "G: [\n",
       "  add_out2_e <= 0.076904296875\n",
@@ -1335,56 +1635,64 @@
     }
    ],
    "source": [
-    "in1 = PortWordLength(n=6, p=0, e=0, name=\"in1\")\n",
-    "in2 = PortWordLength(n=6, p=0, e=0, name=\"in2\")\n",
-    "in3 = PortWordLength(n=6, p=0, e=0, name=\"in3\")\n",
-    "const1 = PortWordLength(n=6, p=0, name=\"const1\")\n",
-    "const2 = PortWordLength(n=6, p=0, name=\"const2\")\n",
-    "const3 = PortWordLength(n=6, p=0, name=\"const3\")\n",
-    "mult_out1 = PortWordLength(n=6, p=0, name=\"mult_out1\")\n",
-    "mult_out2 = PortWordLength(n=6, p=0, name=\"mult_out2\")\n",
-    "mult_out3 = PortWordLength(n=6, p=0, name=\"mult_out3\")\n",
-    "add_out1 = PortWordLength(n=6, p=0, name=\"add_out1\")\n",
-    "add_out2 = PortWordLength(n=6, p=0, name=\"add_out2\")\n",
-    "\n",
-    "const1.set_value(float_to_bin(0.2, const1))\n",
-    "const2.set_value(float_to_bin(0.6, const2))\n",
-    "const3.set_value(float_to_bin(0.2, const3))\n",
-    "const1.set_e(0.2 - const1.value_num())\n",
-    "const2.set_e(0.6 - const2.value_num())\n",
-    "const3.set_e(0.2 - const3.value_num())\n",
-    "\n",
-    "print(f\"truncated coefficient: 0.2 to {const1.value_num()}\")\n",
-    "print(f\"truncated coefficient: 0.6 to {const2.value_num()}\")\n",
-    "print(f\"truncated coefficient: 0.2 to {const3.value_num()}\")\n",
-    "\n",
-    "c1 = form_contract_mult_const(in_port1=in1, in_port_const=const1, out_port=mult_out1)\n",
-    "c2 = form_contract_mult_const(in_port1=in2, in_port_const=const2, out_port=mult_out2)\n",
-    "c3 = form_contract_mult_const(in_port1=in3, in_port_const=const3, out_port=mult_out3)\n",
-    "\n",
-    "ci1 = form_contract_input(in_port=in1)\n",
-    "ci2 = form_contract_input(in_port=in2)\n",
-    "ci3 = form_contract_input(in_port=in3)\n",
-    "\n",
-    "contract1 = PolyhedralContract.from_dict(c1)\n",
-    "contract2 = PolyhedralContract.from_dict(c2)\n",
-    "contract3 = PolyhedralContract.from_dict(c3)\n",
-    "contract_i1 = PolyhedralContract.from_dict(ci1)\n",
-    "contract_i2 = PolyhedralContract.from_dict(ci2)\n",
-    "contract_i3 = PolyhedralContract.from_dict(ci3)\n",
-    "c4 = form_contract_add(in_port1 = mult_out1, in_port2=mult_out2, out_port=add_out1)\n",
-    "c5 = form_contract_add(in_port1 = add_out1, in_port2=mult_out3, out_port=add_out2)\n",
-    "contract4 = PolyhedralContract.from_dict(c4)\n",
-    "contract5 = PolyhedralContract.from_dict(c5)\n",
-    "\n",
-    "contract_system = contract_i1.compose(contract1)\n",
-    "contract_system = contract_system.compose(contract_i2)\n",
-    "contract_system = contract_system.compose(contract2)\n",
-    "contract_system = contract_system.compose(contract_i3)\n",
-    "contract_system = contract_system.compose(contract3)\n",
-    "contract_system = contract_system.compose(contract4)\n",
-    "contract_system = contract_system.compose(contract5)\n",
-    "print(str(contract_system))\n"
+    "def example2_filter_design() -> None:\n",
+    "    \"\"\"Example showing the composition of the filter design\"\"\"\n",
+    "    in1 = PortWordLength(n=6, p=0, e=0, name=\"in1\")\n",
+    "    in2 = PortWordLength(n=6, p=0, e=0, name=\"in2\")\n",
+    "    in3 = PortWordLength(n=6, p=0, e=0, name=\"in3\")\n",
+    "    const1 = PortWordLength(n=6, p=0, name=\"const1\")\n",
+    "    const2 = PortWordLength(n=6, p=0, name=\"const2\")\n",
+    "    const3 = PortWordLength(n=6, p=0, name=\"const3\")\n",
+    "    mult_out1 = PortWordLength(n=6, p=0, name=\"mult_out1\")\n",
+    "    mult_out2 = PortWordLength(n=6, p=0, name=\"mult_out2\")\n",
+    "    mult_out3 = PortWordLength(n=6, p=0, name=\"mult_out3\")\n",
+    "    add_out1 = PortWordLength(n=6, p=0, name=\"add_out1\")\n",
+    "    add_out2 = PortWordLength(n=6, p=0, name=\"add_out2\")\n",
+    "\n",
+    "    a = 0.2\n",
+    "    b = 0.6\n",
+    "    c = 0.2\n",
+    "    const1.value = float_to_bin(a, const1)\n",
+    "    const2.value = float_to_bin(b, const2)\n",
+    "    const3.value = float_to_bin(c, const3)\n",
+    "    const1.e = a - const1.value_num()\n",
+    "    const2.e = b - const2.value_num()\n",
+    "    const3.e = c - const3.value_num()\n",
+    "\n",
+    "    print(f\"truncated coefficient: {a} to {const1.value_num()}\")\n",
+    "    print(f\"truncated coefficient: {b} to {const2.value_num()}\")\n",
+    "    print(f\"truncated coefficient: {c} to {const3.value_num()}\")\n",
+    "\n",
+    "    c1 = form_contract_mult_const(in_port=in1, in_port_const=const1, out_port=mult_out1)\n",
+    "    c2 = form_contract_mult_const(in_port=in2, in_port_const=const2, out_port=mult_out2)\n",
+    "    c3 = form_contract_mult_const(in_port=in3, in_port_const=const3, out_port=mult_out3)\n",
+    "\n",
+    "    ci1 = form_contract_input(in_port=in1)\n",
+    "    ci2 = form_contract_input(in_port=in2)\n",
+    "    ci3 = form_contract_input(in_port=in3)\n",
+    "\n",
+    "    contract1 = PolyhedralContract.from_dict(c1)\n",
+    "    contract2 = PolyhedralContract.from_dict(c2)\n",
+    "    contract3 = PolyhedralContract.from_dict(c3)\n",
+    "    contract_i1 = PolyhedralContract.from_dict(ci1)\n",
+    "    contract_i2 = PolyhedralContract.from_dict(ci2)\n",
+    "    contract_i3 = PolyhedralContract.from_dict(ci3)\n",
+    "    c4 = form_contract_add(in_port1=mult_out1, in_port2=mult_out2, out_port=add_out1)\n",
+    "    c5 = form_contract_add(in_port1=add_out1, in_port2=mult_out3, out_port=add_out2)\n",
+    "    contract4 = PolyhedralContract.from_dict(c4)\n",
+    "    contract5 = PolyhedralContract.from_dict(c5)\n",
+    "\n",
+    "    contract_system = contract_i1.compose(contract1)\n",
+    "    contract_system = contract_system.compose(contract_i2)\n",
+    "    contract_system = contract_system.compose(contract2)\n",
+    "    contract_system = contract_system.compose(contract_i3)\n",
+    "    contract_system = contract_system.compose(contract3)\n",
+    "    contract_system = contract_system.compose(contract4)\n",
+    "    contract_system = contract_system.compose(contract5)\n",
+    "    print(str(contract_system))\n",
+    "\n",
+    "\n",
+    "example2_filter_design()"
    ]
   },
   {
@@ -1414,50 +1722,71 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "0.06874999999999998 0.828125 0.578125 0.828125 0.921875\n"
+      "0.06874999999999998 0.828125 0.890625 0.90625 0.90625\n"
      ]
     }
    ],
    "source": [
-    "def enumerate_error():\n",
-    "    diff_max = 0\n",
-    "    a_max = 0\n",
-    "    i1_max=0\n",
-    "    i2_max=0\n",
-    "    i3_max=0\n",
-    "    for i1 in range(0, 2**6):\n",
-    "        i1_float = i1/2**6\n",
+    "def example_enumerate_error() -> None:\n",
+    "    \"\"\"Find the actual bound by enumerating the input combinations\"\"\"\n",
+    "    in1 = PortWordLength(n=6, p=0, e=0, name=\"in1\")\n",
+    "    in2 = PortWordLength(n=6, p=0, e=0, name=\"in2\")\n",
+    "    in3 = PortWordLength(n=6, p=0, e=0, name=\"in3\")\n",
+    "    const1 = PortWordLength(n=6, p=0, name=\"const1\")\n",
+    "    const2 = PortWordLength(n=6, p=0, name=\"const2\")\n",
+    "    const3 = PortWordLength(n=6, p=0, name=\"const3\")\n",
+    "    mult_out1 = PortWordLength(n=6, p=0, name=\"mult_out1\")\n",
+    "    mult_out2 = PortWordLength(n=6, p=0, name=\"mult_out2\")\n",
+    "    mult_out3 = PortWordLength(n=6, p=0, name=\"mult_out3\")\n",
+    "    add_out1 = PortWordLength(n=6, p=0, name=\"add_out1\")\n",
+    "    add_out2 = PortWordLength(n=6, p=0, name=\"add_out2\")\n",
+    "\n",
+    "    a = 0.2\n",
+    "    b = 0.6\n",
+    "    c = 0.2\n",
+    "    const1.value = float_to_bin(a, const1)\n",
+    "    const2.value = float_to_bin(b, const2)\n",
+    "    const3.value = float_to_bin(c, const3)\n",
+    "\n",
+    "    diff_max: float = 0\n",
+    "    a_max: float = 0\n",
+    "    i1_max: float = 0\n",
+    "    i2_max: float = 0\n",
+    "    i3_max: float = 0\n",
+    "    for i1, i2, i3 in itertools.combinations(range(0, 2**6), 3):\n",
+    "        i1_float = i1 / 2**6\n",
     "        in1_a = float_to_bin(i1_float, in1)\n",
-    "        in1.set_value(in1_a)\n",
-    "        for i2 in range(0, 2**6):\n",
-    "            i2_float = i2/2**6\n",
-    "            in2_a = float_to_bin(i2_float, in2)\n",
-    "            in2.set_value(in2_a)\n",
-    "            for i3 in range(0, 2**6):\n",
-    "                i3_float = i3/2**6\n",
-    "                in3_a = float_to_bin(i3_float, in3)\n",
-    "                in3.set_value(in3_a)\n",
-    "\n",
-    "                port_mult(in1, const1, mult_out1)\n",
-    "                port_mult(in2, const2, mult_out2)\n",
-    "                port_mult(in3, const3, mult_out3)\n",
-    "                port_add(mult_out1, mult_out2, add_out1)\n",
-    "                port_add(add_out1, mult_out3, add_out2)\n",
-    "                actual_result = add_out2.value_num()\n",
-    "                ideal_result = i1_float * 0.2 + i2_float * 0.6 + i3_float * 0.2\n",
-    "                diff = ideal_result - actual_result\n",
-    "\n",
-    "                if diff > diff_max:\n",
-    "                    diff_max = diff\n",
-    "                    i1_max = i1_float\n",
-    "                    i2_max = i2_float\n",
-    "                    i3_max = i3_float\n",
-    "                if actual_result > a_max:\n",
-    "                    a_max = actual_result\n",
-    "    return diff_max, i1_max, i2_max, i3_max, a_max\n",
-    "\n",
-    "diff_max, i1_max, i2_max, i3_max, a_max = enumerate_error()\n",
-    "print(diff_max, i1_max, i2_max, i3_max, a_max)"
+    "        in1.value = in1_a\n",
+    "\n",
+    "        i2_float = i2 / 2**6\n",
+    "        in2_a = float_to_bin(i2_float, in2)\n",
+    "        in2.value = in2_a\n",
+    "\n",
+    "        i3_float = i3 / 2**6\n",
+    "        in3_a = float_to_bin(i3_float, in3)\n",
+    "        in3.value = in3_a\n",
+    "\n",
+    "        port_mult(in1, const1, mult_out1)\n",
+    "        port_mult(in2, const2, mult_out2)\n",
+    "        port_mult(in3, const3, mult_out3)\n",
+    "        port_add(mult_out1, mult_out2, add_out1)\n",
+    "        port_add(add_out1, mult_out3, add_out2)\n",
+    "        actual_result = add_out2.value_num()\n",
+    "        ideal_result = i1_float * a + i2_float * b + i3_float * c\n",
+    "        diff = ideal_result - actual_result\n",
+    "\n",
+    "        if diff > diff_max:\n",
+    "            diff_max = diff\n",
+    "            i1_max = i1_float\n",
+    "            i2_max = i2_float\n",
+    "            i3_max = i3_float\n",
+    "        if actual_result > a_max:\n",
+    "            a_max = actual_result\n",
+    "\n",
+    "    print(diff_max, i1_max, i2_max, i3_max, a_max)\n",
+    "\n",
+    "\n",
+    "example_enumerate_error()"
    ]
   },
   {
diff --git a/case_studies/digital_signal_processing/dsp_wl/figures/digital_data_path_example.png b/case_studies/digital_signal_processing/dsp_wl/figures/digital_data_path_example.png
deleted file mode 100644
index 5c4bf7d2..00000000
Binary files a/case_studies/digital_signal_processing/dsp_wl/figures/digital_data_path_example.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/dsp_wl/figures/digital_filter_flow.png b/case_studies/digital_signal_processing/dsp_wl/figures/digital_filter_flow.png
deleted file mode 100644
index 58472be4..00000000
Binary files a/case_studies/digital_signal_processing/dsp_wl/figures/digital_filter_flow.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/dsp_wl/figures/example1.png b/case_studies/digital_signal_processing/dsp_wl/figures/example1.png
deleted file mode 100644
index faefac93..00000000
Binary files a/case_studies/digital_signal_processing/dsp_wl/figures/example1.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/dsp_wl/figures/type_of_error.png b/case_studies/digital_signal_processing/dsp_wl/figures/type_of_error.png
deleted file mode 100644
index 6d6c20c8..00000000
Binary files a/case_studies/digital_signal_processing/dsp_wl/figures/type_of_error.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/dsp_wl_backup.ipynb b/case_studies/digital_signal_processing/dsp_wl_backup.ipynb
deleted file mode 100644
index 941d663b..00000000
--- a/case_studies/digital_signal_processing/dsp_wl_backup.ipynb
+++ /dev/null
@@ -1,1631 +0,0 @@
-{
- "cells": [
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "37d4b21e",
-   "metadata": {},
-   "source": [
-    "## Introduction\n",
-    "In this example, we show that how gear can be applied to find the bounds of error of a digital signal processing circuit throught the fixed-number lengths of the internal calculation.\n",
-    "\n",
-    "The errors of computation of a digital signal processing circuit come from three sources: truncation error, quantization error, and inaccurate source.\n",
-    "1.  Truncation Error:\n",
-    "The truncation error is the error when a fixed-point number is converted from a longer representation to a shorter representation, which means the bits are not sufficient to represent the number, and thus the number must be truncated.\n",
-    "The truncation error occur when the result of an operation is connect to a signal with different word length.\n",
-    "2. Quantization Error:\n",
-    "The quantization error occurs when the an analog signal is converted into a digital signal, or an algorithm requires real numbers as its coefficients. As the fixed number cannot represent any real number with infinite precision. The number has to be quantized and thus are not as the exact value as the analog signal.\n",
-    "\n",
-    "3. Inaccurate Source:\n",
-    "This error comes from the measurement in the analog signal because of noise and non-ideal effect on the sensor, and could be seen as a random noise on the analog signal.\n",
-    "\n",
-    "The determination of the number of bits for fixed-point number requires tradeoff between the accuracy and the performance of the circuit.\n",
-    "Power, timing, and area of the circuit can benefit from reducing the number of bits for representation of fixed-point number as the gate count can be reduced, which results in shorter critical path length for optimization and smaller core footprint.\n",
-    "However, the accuracy of the signal processing decreases as more error is introduced in the computation and propagated to the output of the circuit.\n",
-    "\n",
-    "Therefore, in digital signal processing circuit design. Exploration of the word lengths for all fixed-point number is a crucial stage from algorithm design to circuit implementation for optimizing the circuit performance while satisfying the system requirements.\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "5d6da13b",
-   "metadata": {},
-   "source": [
-    "## Preliminaries\n",
-    "#### Representation of Fixed-point Number\n",
-    "\n",
-    "A word length of a fixed-point number can be described as a tuple $(n, p)$, where $n$ denotes the number of bits, and $p$ is the position of the binary point, with 0 position means the position before the most significant bit.\n",
-    "\n",
-    "For example, A fixed-point number with word length $(5,2)$ with bit value \"11011\" means the binary number $11.011$, which is equivalent to $3.375$ in decimal."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "0cc1ddb8",
-   "metadata": {},
-   "source": [
-    "First, we import some data structure for holding the wordlength, and the apis of gear."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "aa002743",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "from gear.terms.polyhedra.loaders import readContract, writeContract\n",
-    "from tool import PortWordLength\n",
-    "import numpy as np"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "9659ebc0",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "The word length (n = 5, p = 2) of the bits \"11011\" is: \n",
-      "3.375\n"
-     ]
-    }
-   ],
-   "source": [
-    "# Definition of value_num method in PortWordLength class\n",
-    "#    def value_num(self):\n",
-    "#        return int(self.value, base=2) * 2 ** (- (self.n - self.p))\n",
-    "\n",
-    "\n",
-    "port = PortWordLength(n=5, p=2, value=\"11011\")\n",
-    "print(f\"The word length (n = {port.n}, p = {port.p}) of the bits \\\"{port.value}\\\" is: \")\n",
-    "print(port.value_num())"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "78846570",
-   "metadata": {},
-   "source": [
-    "## Forming contract for capturing error of fixed-point number\n",
-    "\n",
-    "Here we introduce how to form the contract for the fixed-point number operation.\n",
-    "First, we need to model how the error is generated.\n",
-    "Then we consider the propagation of the error.\n",
-    "We formulate the contract by combining the generation of error and propagation of the error."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "af36060d",
-   "metadata": {},
-   "source": [
-    "#### Error Modeling\n",
-    "Each operation can be seen as a two-staged process.\n",
-    "First, an ideal output with certian wordlength is generated without losing any information, given the word lengths of all input ports.\n",
-    "Then the ideal output is truncated to the actual output port and could result in truncation error.\n",
-    "Therefore, the modeling of the ideal output wordlength and the truncation error is required to formulate the contract."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "f07d53c1",
-   "metadata": {},
-   "source": [
-    "#### Truncation Error Modeling\n",
-    "In the following, we details how to model the truncation error.\n",
-    "\n",
-    "##### Case 1. Binary Point Position Unchanged\n",
-    "In this case the change of word length only result in losing the lowest significant bits.\n",
-    "The maximal truncation error from the word length $(n_1, p)$ to the word length $(n_2, p)$ is $2^{p}(2^{-n_2}-2^{-n_1})$, when $n_1 > n_2$.\n",
-    "If $n_1 <= n_2$, there is no truncation and therefore the error is exactly $0$."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "dc165ff9",
-   "metadata": {},
-   "source": [
-    "The following example shows how the error is bounded:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "cd58d386",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def truncation_error_same_position(pi, po):\n",
-    "    assert(pi.p == po.p)\n",
-    "    if pi.n > po.n:\n",
-    "        return 2 ** po.p * (2 ** (-po.n) - 2 ** (-pi.n))\n",
-    "    else :\n",
-    "        return 0\n",
-    "\n",
-    "def truncate(pi, po):\n",
-    "    # separate the bits into two parts, before binary point and after binary point\n",
-    "    bits_before_point = pi.value[:pi.p]\n",
-    "    bits_after_point = pi.value[pi.p:]\n",
-    "    \n",
-    "    # truncate or appending 0 \n",
-    "    if po.p >= pi.p:\n",
-    "        bits_before_point = (\"0\" * (po.p - pi.p)) + bits_before_point\n",
-    "    else:\n",
-    "        bits_before_point = bits_before_point[pi.p - po.p:]\n",
-    "\n",
-    "    if po.n - po.p >= pi.n - pi.p:\n",
-    "        bits_after_point = bits_after_point + \"0\" * (po.n - po.p - pi.n + pi.p)\n",
-    "    else:\n",
-    "        bits_after_point = bits_after_point[:(po.n - po.p)]\n",
-    "\n",
-    "    # combine the both parts\n",
-    "    ret = bits_before_point + bits_after_point\n",
-    "    po.set_value(value = ret)\n",
-    "    return po.value"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "13bee31b",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Truncation Error: \n",
-      "0.09375\n",
-      "Example Value to produce the error\n",
-      "p1 = 3.96875, bits: 1111111\n",
-      "p2 = 3.875, bits: 11111\n",
-      "p1 - p2 =  0.09375\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=2, name=\"p1\", value = \"1111111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "\n",
-    "print(\"Truncation Error: \")\n",
-    "print(truncation_error_same_position(p1, p2))\n",
-    "print(\"Example Value to produce the error\")\n",
-    "\n",
-    "truncate(p1, p2)\n",
-    "print(f\"p1 = {p1.value_num()}, bits: {p1.value}\")\n",
-    "print(f\"p2 = {p2.value_num()}, bits: {p2.value}\")\n",
-    "print(f\"p1 - p2 = \", p1.value_num() - p2.value_num())"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "1ba6de6f",
-   "metadata": {},
-   "source": [
-    "Then let's see what happen if the binary point position is changed\n",
-    "##### Case 2. Binary Point Position Change\n",
-    "Consider the case when $(n_1, p_1)$ to the word length $(n_2, p_2)$\n",
-    "If the binary point position is change and result in less bits before the binary point ($p_1 > p_2$), the significant bits in the initial representation will be lost. \n",
-    "Therefore, it is reasonable to assume that we know exactly the maximum number of the initial number should not set bits that cannot be included in $2^{p_2}$, which sets an assumption that the initial number must be strictly less than $2^{p_2}$.\n",
-    "Then, we can see the fixed-point number as if the those uneccesary most significant bits ($p_1 - p_2$) can be discarded.\n",
-    "$(n_1, p_1)$ can be seen as $(n_1 - p_1 + p_2, p_2)$. \n",
-    "Then it become the case one, only with the additional assumption."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "489baea4",
-   "metadata": {},
-   "source": [
-    "The following example shows how the error can be calculated."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "478aec9d",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def truncation_error(pi, po):\n",
-    "    # remove uneccesary most significant bits\n",
-    "    # Remider: The assumption must hold!!\n",
-    "    pi_adjusted = PortWordLength(n=pi.n - pi.p + po.p, p=po.p)\n",
-    "    # return the truncation error as in case 1\n",
-    "    return truncation_error_same_position(pi=pi_adjusted, po=po)\n",
-    "\n",
-    "def get_assumption_value(pi, po):\n",
-    "    if pi.p > po.p:\n",
-    "        return 2 ** po.p\n",
-    "    else:\n",
-    "        return float(\"inf\") # no additional assumption needed\n",
-    "\n",
-    "def check_assumption_value(pi, po):\n",
-    "    assumption_value = get_assumption_value(pi, po)\n",
-    "    print(f\"Assumption: {pi.name} < {assumption_value}\")\n",
-    "    if not pi.value_num() < assumption_value:\n",
-    "        print(\"Assumption failed, truncation of MSB occurs\")\n",
-    "        return False\n",
-    "    else:\n",
-    "        print(f\"Assumption Satisfied ({pi.name} = {pi.value_num()} < {assumption_value})\")\n",
-    "        return True\n",
-    "        \n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "9a24f2bb",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Assumption: p1 < 4\n",
-      "Assumption Satisfied (p1 = 2.4375 < 4)\n",
-      "Truncation Error: \n",
-      "0.0625\n",
-      "Example Value to produce the error\n",
-      "p1 = 2.4375, bits: 0100111\n",
-      "p2 = 2.375, bits: 10011\n",
-      "p1 - p2 =  0.0625\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "\n",
-    "assert(check_assumption_value(pi=p1, po=p2))\n",
-    "\n",
-    "print(\"Truncation Error: \")\n",
-    "print(truncation_error(p1, p2))\n",
-    "print(\"Example Value to produce the error\")\n",
-    "\n",
-    "truncate(p1, p2)\n",
-    "print(f\"p1 = {p1.value_num()}, bits: {p1.value}\")\n",
-    "print(f\"p2 = {p2.value_num()}, bits: {p2.value}\")\n",
-    "print(f\"p1 - p2 = \", p1.value_num() - p2.value_num())"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "2bfb710e",
-   "metadata": {},
-   "source": [
-    "#### Ideal Output Wordlength\n",
-    "After we analyze the truncation error, the next problem is what is the wordlength before truncation?\n",
-    "Each operation requires an minimal ideal output wordlength to fully hold the resulting value without losing any information.\n",
-    "We represent the ideal output wordlength using $(n^*, p^*)$.\n",
-    "This is the wordlength truncated before sending to the output port.\n",
-    "And $(n_1, p_1), (n_2, n_2)$ denotes the wordlength of two inputs.\n",
-    "\n",
-    "The ideal output wordlength depends on the type of operation\n",
-    "Let's first examine the addition and then the multiplication"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "7397b1b8",
-   "metadata": {},
-   "source": [
-    "##### Addition\n",
-    "The following equations show the ideal output wordlnegth for addition.\n",
-    "\n",
-    "$n^* = \\max(n_1, n_2 - p_2 + p_1) + \\min(0, p_1 - p_2) + 1$,\n",
-    "\n",
-    "$p* = \\max(p_1, p_2) + 1$.\n",
-    "\n",
-    "The rationale behind the equations is to keep every bits in both inputs and add an additional one bits in the front of the output to handle the carry.\n",
-    "\n",
-    "Using the ideal output wordlength and the above truncation error. We can calculate the error induced by addition"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "b0bc548f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def compute_required_word_length_add(in1: PortWordLength, in2:PortWordLength) -> PortWordLength:\n",
-    "    new_n = max(in1.n, in2.n - in2.p + in1.p) - min(0, in1.p - in2.p) + 1\n",
-    "    new_p = max(in1.p, in2.p) + 1\n",
-    "    return PortWordLength(n=new_n, p=new_p)\n",
-    "\n",
-    "def error_truncation_add(p1, p2, po):\n",
-    "    print(f\"Input {p1.name}: (n={p1.n}, p={p1.p})\")\n",
-    "    print(f\"Input {p2.name}: (n={p2.n}, p={p2.p})\")\n",
-    "    p_ideal = compute_required_word_length_add(in1=p1, in2=p2)\n",
-    "    print(f\"Ideal Output: (n={p_ideal.n}, p={p_ideal.p})\")\n",
-    "    print(f\"Actual Output ({po.name}): (n={po.n}, p={po.p})\")\n",
-    "\n",
-    "    assumption_value = get_assumption_value(pi=p_ideal, po=po)\n",
-    "    print(f\"Assumption: {p1.name} + {p2.name} < {assumption_value}\")\n",
-    "    return truncation_error(pi=p_ideal, po=po)\n",
-    "    "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "2f36d06c",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Input p1: (n=7, p=3)\n",
-      "Input p2: (n=5, p=2)\n",
-      "Ideal Output: (n=8, p=4)\n",
-      "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 + p2 < 8\n",
-      "Error of the addition calculation: 0.1875\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
-    "\n",
-    "err = error_truncation_add(p1=p1, p2=p2, po=p3)\n",
-    "\n",
-    "print(f\"Error of the addition calculation: {err}\")"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "50d6572b",
-   "metadata": {},
-   "source": [
-    "\n",
-    "##### Multiplication\n",
-    "The following equations show the ideal output wordlnegth for multiplication.\n",
-    "$n^* = n_1 + n_2$,\n",
-    "$p* = p_1, p_2$."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "dea9ad92",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def compute_required_word_length_mult(in1: PortWordLength, in2:PortWordLength) -> PortWordLength:\n",
-    "    new_n = in1.n + in2.n\n",
-    "    new_p = in1.p + in2.p\n",
-    "    return PortWordLength(n=new_n, p=new_p)\n",
-    "\n",
-    "def error_truncation_mult(p1, p2, po):\n",
-    "    print(f\"Input {p1.name}: (n={p1.n}, p={p1.p})\")\n",
-    "    print(f\"Input {p2.name}: (n={p2.n}, p={p2.p})\")\n",
-    "    p_ideal = compute_required_word_length_mult(in1=p1, in2=p2)\n",
-    "    print(f\"Ideal Output: (n={p_ideal.n}, p={p_ideal.p})\")\n",
-    "    print(f\"Actual Output ({po.name}): (n={po.n}, p={po.p})\")\n",
-    "\n",
-    "    assumption_value = get_assumption_value(pi=p_ideal, po=po)\n",
-    "    print(f\"Assumption: {p1.name} * {p2.name} < {assumption_value}\")\n",
-    "    return truncation_error(pi=p_ideal, po=po)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "ba1a5db7",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Input p1: (n=7, p=3)\n",
-      "Input p2: (n=5, p=2)\n",
-      "Ideal Output: (n=12, p=5)\n",
-      "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 * p2 < 8\n",
-      "Error of the multiplication calculation: 0.2421875\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
-    "\n",
-    "err = error_truncation_mult(p1=p1, p2=p2, po=p3)\n",
-    "\n",
-    "print(f\"Error of the multiplication calculation: {err}\")"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "c5b0fd1f",
-   "metadata": {},
-   "source": [
-    "#### Propagation of Error\n",
-    "\n",
-    "The error $e$ already presents in a signal to the port $p$ will propagate to the final output.\n",
-    "Such error including error from the previus operation, quantization error and known inaccuracies of the input.\n",
-    "This error is independent to the error of truncation error, as truncation error compares the value of the output to the nominal value of the input signal.\n",
-    "As a result, the error can be represented as the sum of the propagation error and the truncation error in the stage: \n",
-    "\n",
-    "$e = e_p + e_t$,\n",
-    "\n",
-    "where $e_p$ is the propagtion error from inputs regardless of how many bits we have in the output, and $e_t$ denotes the truncation error that occurs due to the descrepancy between the wordlength of the ideal output and that of the actual output.\n",
-    "Thus, it is important to explore differnet word length in the circuit, as one change to the word length affect the accuracy of the output.\n",
-    "\n",
-    "Let's now see how the error is propagated in different operation"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "eee89085",
-   "metadata": {},
-   "source": [
-    "##### Propagation of Error in Addition\n",
-    "The error is exactly the sum of error from both inputs.\n",
-    "$e_p = e_1 + e_2$\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "36582cb6",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def error_propagation_add(p1, p2, po):\n",
-    "    return p1.e + p2.e"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "id": "3719ba6f",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Input p1: (n=7, p=3)\n",
-      "Input p2: (n=5, p=2)\n",
-      "Ideal Output: (n=8, p=4)\n",
-      "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 + p2 < 8\n",
-      "Propagation error: 0.7\n",
-      "Truncation error: 0.1875\n",
-      "Error bound of the addition calculation: 0.8875\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=3, e=0.5, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, e=0.2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
-    "\n",
-    "e_p = error_propagation_add(p1=p1, p2=p2, po=p3)\n",
-    "e_t = error_truncation_add(p1=p1, p2=p2, po=p3)\n",
-    "\n",
-    "err = e_p + e_t\n",
-    "print(f\"Propagation error: {e_p}\")\n",
-    "print(f\"Truncation error: {e_t}\")\n",
-    "print(f\"Error bound of the addition calculation: {err}\")"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "d25836eb",
-   "metadata": {},
-   "source": [
-    "##### Propagation of Error in Multiplication\n",
-    "The error is as follows:\n",
-    "\n",
-    "$e_p = a_1*e_2 + a_2*e_1 - e_1 * e_2$, \n",
-    "\n",
-    "where $a_1$ is the maximal possible value from input $1$, $a_2$ is the maximum possible value from input $2$.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "5613e607",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def get_actual_possible_value(in_port: PortWordLength):\n",
-    "    return (1 - (2 ** -in_port.n) )* 2 ** in_port.p\n",
-    "\n",
-    "def error_propagation_mult(p1, p2, po):\n",
-    "    return p1.a * p2.e + p2.a * p1.e + p1.e * p2.e"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "6497f4b4",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Input p1: (n=7, p=3)\n",
-      "Input p2: (n=5, p=2)\n",
-      "Ideal Output: (n=12, p=5)\n",
-      "Actual Output (p3): (n=5, p=3)\n",
-      "Assumption: p1 * p2 < 8\n",
-      "Propagation error: 2.81875\n",
-      "Truncation error: 0.2421875\n",
-      "Error bound of the multiplication calculation: 3.0609375\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=3, e=0.5, name=\"p1\", value = \"0100111\")\n",
-    "p2 = PortWordLength(n=5, p=2, e=0.3, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
-    "\n",
-    "e_p = error_propagation_mult(p1=p1, p2=p2, po=p3)\n",
-    "e_t = error_truncation_mult(p1=p1, p2=p2, po=p3)\n",
-    "\n",
-    "err = e_p + e_t\n",
-    "print(f\"Propagation error: {e_p}\")\n",
-    "print(f\"Truncation error: {e_t}\")\n",
-    "print(f\"Error bound of the multiplication calculation: {err}\")"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "a90f8c44",
-   "metadata": {},
-   "source": [
-    "##### General operation\n",
-    "Using the above analysis, we now formulate the contract for different operation.\n",
-    "The error of any general operations with arity n, denoted by $f(x_1, x_2,..., x_n)$ is as follows:\n",
-    "\n",
-    "$e_p = f(x_1, x_2,..., x_n)) - f(x_1 - e_1, x_2 - e_2,..., x_n - e_n)$ "
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "84358426",
-   "metadata": {},
-   "source": [
-    "#### Contract Formulation\n",
-    "Combining the error bound modeling from propagation and truncation. We can formalte contracts for each operation.\n",
-    "A contract is a pair of assumption and guarantee, denoted by $C = (A, G)$, where $C$ represents the contract, $A$ denotes its assumption, and $G$ is the guarantee.\n",
-    "In the following, we introduce how the contract is formulated for addition and multiplication.\n",
-    "\n",
-    "##### Variables in the contract\n",
-    "To describe the behavior of the operations, we encode two variables for each port: the actual value and the error.\n",
-    "The actual value means the nominal value appears at the port instead of the ideal value we hope to use in the signal processing algorithm.\n",
-    "The error is the deviation of the nominal value to the ideal value in the signal processing algorithm.\n",
-    "\n",
-    "##### Assumption\n",
-    "The only source of assumption is the assumption in truncation to prevent losing significant bits when the binary point positions of the ideal output and the truncated output is different.\n",
-    "\n",
-    "##### Guarantee\n",
-    "The guarantee of the contract includees two part. The first part describes the error of the output port, and the second part ensures the range of the actual value in the output port."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "f6001d88",
-   "metadata": {},
-   "source": [
-    "The following functions show how we form contract for arbitrary length of input ports and output ports.\n",
-    "The \"_a\" variables are used as the variable for the actual value going through the port."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "7ec7baf9",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def form_contract_add(in_port1, in_port2, out_port):\n",
-    "    ret_contract = {}\n",
-    "    # define input/output vars\n",
-    "    ret_contract[\"input_vars\"] = [f\"{in_port1.name}_a\", f\"{in_port1.name}_e\",\n",
-    "                                 f\"{in_port2.name}_a\", f\"{in_port2.name}_e\"]\n",
-    "    ret_contract[\"output_vars\"] = [f\"{out_port.name}_a\", f\"{out_port.name}_e\"]\n",
-    "    # get assumption\n",
-    "    ideal_out_port = compute_required_word_length_add(in1=in_port1, in2=in_port2)\n",
-    "    assumption_value = get_assumption_value(pi=ideal_out_port, po=out_port)\n",
-    "    # write assumption in the contract\n",
-    "    if assumption_value != float(\"inf\"):\n",
-    "        ret_contract[\"assumptions\"] =  [{\"coefficients\":{f\"{in_port1.name}_a\":1, f\"{in_port2.name}_a\":1},\n",
-    "                                    \"constant\":assumption_value}]\n",
-    "    else:\n",
-    "        ret_contract[\"assumptions\"] = []\n",
-    "    # get guarantee\n",
-    "    e_t = truncation_error(pi=ideal_out_port, po=out_port)\n",
-    "\n",
-    "    # write guarantee in the contract, note the propagation is encoded in the polyhedral constraints\n",
-    "    ret_contract[\"guarantees\"] =  [{\"coefficients\":{f\"{in_port1.name}_e\":-1, f\"{in_port2.name}_e\":-1, f\"{out_port.name}_e\": 1},\n",
-    "                                \"constant\":e_t},\n",
-    "                                {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":out_port.a},\n",
-    "                                {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":in_port1.a + in_port2.a},\n",
-    "                                {\"coefficients\":{f\"{out_port.name}_a\": 1, f\"{in_port1.name}_a\": -1, f\"{in_port2.name}_a\": -1}, \"constant\":0}\n",
-    "                                ]\n",
-    "    return ret_contract\n",
-    "\n",
-    "\n",
-    "def form_contract_mult_const(in_port1, in_port_const, out_port):\n",
-    "    ret_contract = {}\n",
-    "    # define input/output vars\n",
-    "    ret_contract[\"input_vars\"] = [f\"{in_port1.name}_a\", f\"{in_port1.name}_e\"]\n",
-    "    ret_contract[\"output_vars\"] = [f\"{out_port.name}_a\", f\"{out_port.name}_e\"]\n",
-    "    # get assumption\n",
-    "    ideal_out_port = compute_required_word_length_mult(in1=in_port1, in2=in_port_const)\n",
-    "    assumption_value = get_assumption_value(pi=ideal_out_port, po=out_port)\n",
-    "    # write assumption in the contract\n",
-    "    if assumption_value != float(\"inf\"):\n",
-    "        print(assumption_value)\n",
-    "        ret_contract[\"assumptions\"] =  [{\"coefficients\":{f\"{in_port1.name}_a\": in_port_const.a},\n",
-    "                                    \"constant\":assumption_value}]\n",
-    "    else:\n",
-    "        ret_contract[\"assumptions\"] = []\n",
-    "    # get guarantee\n",
-    "    print(ideal_out_port.to_string())\n",
-    "    e_t = truncation_error(pi=ideal_out_port, po=out_port)\n",
-    "\n",
-    "    # write guarantee in the contract, note the propagation is encoded in the polyhedral constraints\n",
-    "    ret_contract[\"guarantees\"] =  [{\"coefficients\":\n",
-    "                                    {   f\"{in_port1.name}_e\":-in_port_const.a+in_port_const.e, \n",
-    "                                        f\"{in_port1.name}_a\":-in_port_const.e,\n",
-    "                                        f\"{out_port.name}_e\": 1},\n",
-    "                                    \"constant\":e_t},\n",
-    "                                    {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":out_port.a},\n",
-    "                                    {\"coefficients\":{f\"{out_port.name}_a\": 1}, \"constant\":in_port_const.a * in_port1.a},\n",
-    "                                    {\"coefficients\":{f\"{out_port.name}_a\": 1, f\"{in_port1.name}_a\": -in_port_const.a}, \"constant\":0}\n",
-    "                                    ]\n",
-    "    return ret_contract"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "a2684ccb",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "{'input_vars': ['p1_a', 'p1_e', 'p2_a', 'p2_e'], 'output_vars': ['p3_a', 'p3_e'], 'assumptions': [{'coefficients': {'p1_a': 1, 'p2_a': 1}, 'constant': 8}], 'guarantees': [{'coefficients': {'p1_e': -1, 'p2_e': -1, 'p3_e': 1}, 'constant': 0.1875}, {'coefficients': {'p3_a': 1}, 'constant': 7.75}, {'coefficients': {'p3_a': 1}, 'constant': 11.8125}, {'coefficients': {'p3_a': 1, 'p1_a': -1, 'p2_a': -1}, 'constant': 0}]}\n",
-      "InVars: [<Var p1_a>, <Var p1_e>, <Var p2_a>, <Var p2_e>]\n",
-      "OutVars:[<Var p3_a>, <Var p3_e>]\n",
-      "A: 1*p1_a + 1*p2_a <= 8\n",
-      "G: -1*p1_e + -1*p2_e + 1*p3_e <= 0.1875, 1*p3_a <= 7.75, -1*p1_a + -1*p2_a + 1*p3_a <= 0.0\n",
-      "8\n",
-      "Port: , (n, p) = (12, 5), e = 0, a = 31.9921875\n",
-      "InVars: [<Var p4_a>, <Var p4_e>]\n",
-      "OutVars:[<Var p6_a>, <Var p6_e>]\n",
-      "A: 3.375*p4_a <= 8\n",
-      "G: -0.03*p4_a + -3.345*p4_e + 1.0*p6_e <= 0.2421875, 1.0*p6_a <= 7.75, -3.375*p4_a + 1.0*p6_a <= 0.0\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1 = PortWordLength(n=7, p=3, name=\"p1\")\n",
-    "p2 = PortWordLength(n=5, p=2, name=\"p2\")\n",
-    "p3 = PortWordLength(n=5, p=3, name=\"p3\")\n",
-    "c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
-    "print(c1)\n",
-    "contract1 = readContract(c1)\n",
-    "print(str(contract1))\n",
-    "\n",
-    "\n",
-    "p4 = PortWordLength(n=7, p=3, name=\"p4\")\n",
-    "p5 = PortWordLength(n=5, p=2, e=0.03, value=\"11011\", name=\"p5\") # const\n",
-    "p6 = PortWordLength(n=5, p=3, name=\"p6\")\n",
-    "c2 = form_contract_mult_const(in_port1=p4, in_port_const=p5, out_port=p6)\n",
-    "contract2 = readContract(c2)\n",
-    "print(str(contract2))"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "9756b6c2",
-   "metadata": {},
-   "source": [
-    "#### Example 1\n",
-    "Consider the following simple system with only two adder.\n",
-    "\n",
-    "![Example 1](figures/example1.png)\n",
-    "\n",
-    "We will show that how gear can be applied to get the system errors."
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "43122d15",
-   "metadata": {},
-   "source": [
-    "First, we express each port using the PortWordLength class, and then form the contracts for the operations."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 32,
-   "id": "690cd39b",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Contract 1:\n",
-      "InVars: [<Var p1_a>, <Var p1_e>, <Var p2_a>, <Var p2_e>]\n",
-      "OutVars:[<Var p3_a>, <Var p3_e>]\n",
-      "A: 1*p1_a + 1*p2_a <= 8\n",
-      "G: -1*p1_e + -1*p2_e + 1*p3_e <= 0.125, 1*p3_a <= 7.75, -1*p1_a + -1*p2_a + 1*p3_a <= 0.0\n",
-      "Contract 2:\n",
-      "InVars: [<Var p3_a>, <Var p3_e>, <Var p4_a>, <Var p4_e>]\n",
-      "OutVars:[<Var p5_a>, <Var p5_e>]\n",
-      "A: 1*p3_a + 1*p4_a <= 8\n",
-      "G: -1*p3_e + -1*p4_e + 1*p5_e <= 0.0625, 1*p5_a <= 7.875, -1*p3_a + -1*p4_a + 1*p5_a <= 0.0\n"
-     ]
-    }
-   ],
-   "source": [
-    "def create_example1():\n",
-    "    p1 = PortWordLength(n=5, p=2, name = \"p1\")\n",
-    "    p2 = PortWordLength(n=5, p=3, name = \"p2\")\n",
-    "    p3 = PortWordLength(n=5, p=3, name = \"p3\")\n",
-    "    c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
-    "\n",
-    "    p4 = PortWordLength(n=7, p=3, name = \"p4\")\n",
-    "    p5 = PortWordLength(n=6, p=3, name = \"p5\")\n",
-    "    c2 = form_contract_add(in_port1=p3, in_port2=p4, out_port=p5)\n",
-    "\n",
-    "    contract1 = readContract(c1)\n",
-    "    contract2 = readContract(c2)\n",
-    "    print(\"Contract 1:\\n\" + str(contract1))\n",
-    "    print(\"Contract 2:\\n\" + str(contract2))\n",
-    "    return contract1, contract2, p1, p2, p3, p4, p5\n",
-    "    \n",
-    "contract1, contract2, p1, p2, p3, p4, p5 = create_example1()"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "67058ba5",
-   "metadata": {},
-   "source": [
-    "We then use the composition to get the system contract."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 33,
-   "id": "bd19b796",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Contract Sys:\n",
-      "InVars: [<Var p1_a>, <Var p1_e>, <Var p2_a>, <Var p2_e>, <Var p4_a>, <Var p4_e>]\n",
-      "OutVars:[<Var p5_a>, <Var p5_e>]\n",
-      "A: 1*p4_a <= 0.250000000000000, 1*p1_a + 1*p2_a <= 8\n",
-      "G: -1*p1_e + -1*p2_e + -1*p4_e + 1*p5_e <= 0.187500000000000, -1*p4_a + 1*p5_a <= 7.75, -1*p1_a + -1*p2_a + -1*p4_a + 1*p5_a <= 0.0, 1*p5_a <= 7.875\n"
-     ]
-    }
-   ],
-   "source": [
-    "contract_sys = contract1.compose(contract2)\n",
-    "print(\"Contract Sys:\\n\" + str(contract_sys))"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "07cfc78b",
-   "metadata": {},
-   "source": [
-    "The system contracts show the relation between the actual value and error bounds in input values.\n",
-    "\n",
-    "If the designers know the context how the system is used, they can includes additional contracts to constrain the input:\n",
-    "\n",
-    "First, we try an example that has no additional contraint on the input, meaning that it can take arbitrary value for the fixed-point number."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "id": "e166f72a",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def form_contract_input(in_port):\n",
-    "    ret_contract = {}\n",
-    "    # define input/output vars\n",
-    "    ret_contract[\"input_vars\"] = []\n",
-    "    ret_contract[\"output_vars\"] = [f\"{in_port.name}_a\", f\"{in_port.name}_e\"]\n",
-    "    # get assumption\n",
-    "    ret_contract[\"assumptions\"] = []\n",
-    "    ret_contract[\"guarantees\"] =  [ {\"coefficients\": {f\"{in_port.name}_a\": 1}, \"constant\": in_port.a},\n",
-    "                                    {\"coefficients\": {f\"{in_port.name}_a\": -1}, \"constant\": 0},\n",
-    "                                    {\"coefficients\": {f\"{in_port.name}_e\": 1}, \"constant\": in_port.e},\n",
-    "                                    {\"coefficients\": {f\"{in_port.name}_e\": -1}, \"constant\": -in_port.e}]\n",
-    "    return ret_contract"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 34,
-   "id": "286e38e8",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Composition Error\n",
-      "The guarantees \n",
-      "1*p2_a <= 7.75, -1*p2_a <= 0.0, 1*p2_e <= 0.0, -1*p2_e <= 0.0\n",
-      "were insufficient to abduce the assumptions \n",
-      "1*p4_a <= 0.250000000000000, 1*p2_a <= 4.12500000000000\n",
-      "by eliminating the variables \n",
-      "[<Var p2_a>, <Var p2_e>, <Var p5_a>, <Var p5_e>]\n"
-     ]
-    }
-   ],
-   "source": [
-    "def test_example_1():\n",
-    "    c_p1 = form_contract_input(in_port=p1)\n",
-    "    c_p2 = form_contract_input(in_port=p2)\n",
-    "    c_p4 = form_contract_input(in_port=p4)\n",
-    "\n",
-    "    contract_p1 = readContract(c_p1)\n",
-    "    contract_p2 = readContract(c_p2)\n",
-    "    contract_p4 = readContract(c_p4)\n",
-    "    \n",
-    "    try: \n",
-    "        contract_sys = contract1.compose(contract2)\n",
-    "        contract_sys = contract_p1.compose(contract_sys)\n",
-    "        contract_sys = contract_p2.compose(contract_sys)\n",
-    "        contract_sys = contract_p4.compose(contract_sys)\n",
-    "        print(\"Contract Sys:\\n\" + str(contract_sys))\n",
-    "        return contract_sys\n",
-    "    except ValueError as e:\n",
-    "        print(\"Composition Error\")\n",
-    "        print(e)\n",
-    "\n",
-    "contract_sys = test_example_1()"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "67da6d8f",
-   "metadata": {},
-   "source": [
-    "The result shows that the guarantees are not sufficient to abduce the assumption.\n",
-    "\n",
-    "It can be observed easily that the input value of `p2_a` = 7.75 would violate the required assumption to prevent MSB from truncated.\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "303b6882",
-   "metadata": {},
-   "source": [
-    "Then we consider the case where certain constraints are known for the inputs:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 35,
-   "id": "7620625b",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Contract Sys:\n",
-      "InVars: []\n",
-      "OutVars:[<Var p5_a>, <Var p5_e>]\n",
-      "A: true\n",
-      "G: 1*p5_a <= 6.00000000000000, 1*p5_e <= 0.187500000000000\n"
-     ]
-    }
-   ],
-   "source": [
-    "p1.set_value(\"10000\")\n",
-    "p2.set_value(\"01111\")\n",
-    "p4.set_value(\"0000100\")\n",
-    "contract_sys = test_example_1()"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "97fc7bd1",
-   "metadata": {},
-   "source": [
-    "Under these sets of constraints on the input, the system is compatible with the environment and thus we obtain the bounds on the actual output and the error bounds to an ideal output. "
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "b156f32a",
-   "metadata": {},
-   "source": [
-    "The contract bring us the benefits of performing more operations on the system.\n",
-    "\n",
-    "For example, we can apply the quotient to find word length for a certain signal.\n",
-    "\n",
-    "Take the system as an example again. If we want to reduce the wordlength of the intermediate signal $P_3$, we can use quotient to get the required wordlength.\n",
-    "\n",
-    "![Example 1](figures/example1.png)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "fbd47751",
-   "metadata": {},
-   "source": [
-    "To use quotient, we starts from the system contract.\n",
-    "The system contract describes what the system looks like without knowing the underlying components.\n",
-    "Let's say we can tolerate an error of the output by 0.1 given the same input constraints for the previous refined one.\n",
-    "\n",
-    "The system contract should be like this:\n",
-    "```\n",
-    "A: True\n",
-    "G: 1*p5_a <= 6.00000000000000, 1*p5_e <= 0.1\n",
-    "```\n",
-    "\n",
-    "We can invoke contract quotient to get the desired system contract:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 38,
-   "id": "9c168335",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "InVars: []\n",
-      "OutVars:[<Var p5_a>, <Var p5_e>]\n",
-      "A: true\n",
-      "G: 1*p5_a <= 6.0, 1*p5_e <= 0.10000000000000009\n"
-     ]
-    }
-   ],
-   "source": [
-    "def get_desired_system_contract():\n",
-    "    ret_contract = {}\n",
-    "    ret_contract[\"input_vars\"] = []\n",
-    "    ret_contract[\"output_vars\"] = [\"p5_a\", \"p5_e\"]\n",
-    "    ret_contract[\"assumptions\"] = []\n",
-    "    ret_contract[\"guarantees\"] = [{\"coefficients\": {f\"p5_a\": 1}, \"constant\": 6.00},\n",
-    "                                  {\"coefficients\": {f\"p5_e\": 1}, \"constant\": 0.1}]\n",
-    "    return readContract(ret_contract)\n",
-    "\n",
-    "contract_spec = get_desired_system_contract()\n",
-    "print(str(contract_spec))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 39,
-   "id": "874e95da",
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "ValueError",
-     "evalue": "not enough values to unpack (expected 2, got 1)",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
-      "Cell \u001b[0;32mIn[39], line 14\u001b[0m\n\u001b[1;32m     11\u001b[0m     tmp_c2 \u001b[39m=\u001b[39m tmp_c1\u001b[39m.\u001b[39mquotient(contract_p2)\n\u001b[1;32m     12\u001b[0m     tmp_c3 \u001b[39m=\u001b[39m tmp_c2\u001b[39m.\u001b[39mquotient(contract_p4)\n\u001b[0;32m---> 14\u001b[0m quotient_example1()\n",
-      "Cell \u001b[0;32mIn[39], line 10\u001b[0m, in \u001b[0;36mquotient_example1\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m contract_p2 \u001b[39m=\u001b[39m readContract(c_p2)\n\u001b[1;32m      8\u001b[0m contract_p4 \u001b[39m=\u001b[39m readContract(c_p4)\n\u001b[0;32m---> 10\u001b[0m tmp_c1 \u001b[39m=\u001b[39m contract_spec\u001b[39m.\u001b[39;49mquotient(contract_p1)\n\u001b[1;32m     11\u001b[0m tmp_c2 \u001b[39m=\u001b[39m tmp_c1\u001b[39m.\u001b[39mquotient(contract_p2)\n\u001b[1;32m     12\u001b[0m tmp_c3 \u001b[39m=\u001b[39m tmp_c2\u001b[39m.\u001b[39mquotient(contract_p4)\n",
-      "File \u001b[0;32m~/shengjungyu/Research/UC_Berkeley/Research/DesignSpecification/Contract/Tools/Contract/gear/src/gear/iocontract/iocontract.py:457\u001b[0m, in \u001b[0;36mIoContract.quotient\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m    455\u001b[0m assumptions \u001b[39m=\u001b[39m copy\u001b[39m.\u001b[39mdeepcopy(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39ma)\n\u001b[1;32m    456\u001b[0m empty_context \u001b[39m=\u001b[39m \u001b[39mtype\u001b[39m(assumptions)(\u001b[39mlist\u001b[39m())\n\u001b[0;32m--> 457\u001b[0m \u001b[39mif\u001b[39;00m assumptions\u001b[39m.\u001b[39;49mrefines(other\u001b[39m.\u001b[39;49ma):\n\u001b[1;32m    458\u001b[0m     assumptions \u001b[39m=\u001b[39m assumptions \u001b[39m|\u001b[39m other\u001b[39m.\u001b[39mg\n\u001b[1;32m    459\u001b[0m assumptions \u001b[39m=\u001b[39m assumptions\u001b[39m.\u001b[39mdeduce_with_context(empty_context, list_union(intvars, outputvars))\n",
-      "File \u001b[0;32m~/shengjungyu/Research/UC_Berkeley/Research/DesignSpecification/Contract/Tools/Contract/gear/src/gear/terms/polyhedra/polyhedra.py:651\u001b[0m, in \u001b[0;36mPolyhedralTermList.refines\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m    649\u001b[0m variables, self_mat, self_cons, ctx_mat, ctx_cons \u001b[39m=\u001b[39m PolyhedralTermList\u001b[39m.\u001b[39mtermlist_to_polytope(\u001b[39mself\u001b[39m, other)\n\u001b[1;32m    650\u001b[0m logging\u001b[39m.\u001b[39mdebug(\u001b[39m\"\u001b[39m\u001b[39mPolytope is \u001b[39m\u001b[39m\\n\u001b[39;00m\u001b[39m%s\u001b[39;00m\u001b[39m\"\u001b[39m, self_mat)\n\u001b[0;32m--> 651\u001b[0m result \u001b[39m=\u001b[39m PolyhedralTermList\u001b[39m.\u001b[39;49mverify_polytope_containment(self_mat, self_cons, ctx_mat, ctx_cons)\n\u001b[1;32m    652\u001b[0m \u001b[39mreturn\u001b[39;00m result\n",
-      "File \u001b[0;32m~/shengjungyu/Research/UC_Berkeley/Research/DesignSpecification/Contract/Tools/Contract/gear/src/gear/terms/polyhedra/polyhedra.py:831\u001b[0m, in \u001b[0;36mPolyhedralTermList.verify_polytope_containment\u001b[0;34m(a_l, b_l, a_r, b_r)\u001b[0m\n\u001b[1;32m    816\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m    817\u001b[0m \u001b[39mSay whether a polytope is contained in another. Both are given in their\u001b[39;00m\n\u001b[1;32m    818\u001b[0m \u001b[39mH-representation.\u001b[39;00m\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    828\u001b[0m \u001b[39m        Vector of H-representation of polytope on RHS of inequality.\u001b[39;00m\n\u001b[1;32m    829\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m    830\u001b[0m \u001b[39m# If the LHS is empty, it is a refinement\u001b[39;00m\n\u001b[0;32m--> 831\u001b[0m \u001b[39mif\u001b[39;00m PolyhedralTermList\u001b[39m.\u001b[39;49mis_polytope_empty(a_l, b_l):\n\u001b[1;32m    832\u001b[0m     \u001b[39mreturn\u001b[39;00m \u001b[39mTrue\u001b[39;00m\n\u001b[1;32m    833\u001b[0m \u001b[39m# If the RHS is empty, but not the LHS, not a refinement\u001b[39;00m\n",
-      "File \u001b[0;32m~/shengjungyu/Research/UC_Berkeley/Research/DesignSpecification/Contract/Tools/Contract/gear/src/gear/terms/polyhedra/polyhedra.py:881\u001b[0m, in \u001b[0;36mPolyhedralTermList.is_polytope_empty\u001b[0;34m(a, b)\u001b[0m\n\u001b[1;32m    870\u001b[0m \u001b[39m@staticmethod\u001b[39m\n\u001b[1;32m    871\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mis_polytope_empty\u001b[39m(a: np\u001b[39m.\u001b[39marray, b: np\u001b[39m.\u001b[39marray) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mbool\u001b[39m:\n\u001b[1;32m    872\u001b[0m     \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m    873\u001b[0m \u001b[39m    Say whether a polytope is empty.\u001b[39;00m\n\u001b[1;32m    874\u001b[0m \n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    879\u001b[0m \u001b[39m            Vector of H-representation of polytope to verify.\u001b[39;00m\n\u001b[1;32m    880\u001b[0m \u001b[39m    \"\"\"\u001b[39;00m\n\u001b[0;32m--> 881\u001b[0m     n, m \u001b[39m=\u001b[39m a\u001b[39m.\u001b[39mshape\n\u001b[1;32m    882\u001b[0m     \u001b[39massert\u001b[39;00m n \u001b[39m==\u001b[39m \u001b[39mlen\u001b[39m(b)\n\u001b[1;32m    883\u001b[0m     \u001b[39mif\u001b[39;00m n \u001b[39m*\u001b[39m m \u001b[39m==\u001b[39m \u001b[39m0\u001b[39m:\n",
-      "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 2, got 1)"
-     ]
-    }
-   ],
-   "source": [
-    "def quotient_example1():\n",
-    "    c_p1 = form_contract_input(in_port=p1)\n",
-    "    c_p2 = form_contract_input(in_port=p2)\n",
-    "    c_p4 = form_contract_input(in_port=p4)\n",
-    "\n",
-    "    contract_p1 = readContract(c_p1)\n",
-    "    contract_p2 = readContract(c_p2)\n",
-    "    contract_p4 = readContract(c_p4)\n",
-    "\n",
-    "    tmp_c1 = contract_spec.quotient(contract_p1)\n",
-    "    tmp_c2 = tmp_c1.quotient(contract_p2)\n",
-    "    tmp_c3 = tmp_c2.quotient(contract_p4)\n",
-    "\n",
-    "quotient_example1()\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "830ca81b",
-   "metadata": {},
-   "source": [
-    "#### Example 2: Filter Design\n",
-    "\n",
-    "We apply the contract to a simple filter that computes the moving average of the signal\n",
-    "\n",
-    "$y[n] = 0.2 \\times x[n-2] + 0.6 \\times x[n-1] + 0.2 \\times x[n] $\n",
-    "\n",
-    "And the following function perform the filter on the signal"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 23,
-   "id": "b6dbabf4",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[0.25 1.2  2.34 3.97 4.94 2.2  0.3 ]\n"
-     ]
-    }
-   ],
-   "source": [
-    "\n",
-    "def example2_filter(x):\n",
-    "    v = np.array([0.2, 0.6, 0.2])\n",
-    "    return np.convolve(x, v)\n",
-    "\n",
-    "print(example2_filter([1.25, 2.25, 3.7, 6.5, 1.5]))\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "b32d9868",
-   "metadata": {},
-   "source": [
-    "If we want to implement this filter in a digital circuit in the transpose form, "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 24,
-   "id": "3c026d5f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import math\n",
-    "def float_to_bin(x, word_length: PortWordLength):\n",
-    "    frac_part, int_part = math.modf(x)\n",
-    "    p = word_length.p\n",
-    "    # get integer part\n",
-    "    if int_part == 0:\n",
-    "        bin_str = \"\"\n",
-    "    else:\n",
-    "        bin_str = bin(int(int_part))[2:]\n",
-    "        if len(bin_str) > p:\n",
-    "            print(\"The port unable to hold the number, significant bits lost\")\n",
-    "        else:\n",
-    "            bin_str = bin_str.zfill(p)\n",
-    "    # get fractional part\n",
-    "    frac_str = \"\"\n",
-    "    req_length = word_length.n - word_length.p\n",
-    "    while len(frac_str) != req_length:\n",
-    "        frac_part *= 2\n",
-    "        if frac_part >= 1:\n",
-    "            frac_str += '1'\n",
-    "            frac_part -= 1\n",
-    "        else:\n",
-    "            frac_str += '0'\n",
-    "\n",
-    "    return bin_str + frac_str\n",
-    "    "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 25,
-   "id": "062572a4",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Port: , (n, p) = (12, 0), e = 0, a = 0.999755859375\n",
-      "Port: , (n, p) = (12, 0), e = 0, a = 0.999755859375\n",
-      "Port: , (n, p) = (12, 0), e = 0, a = 0.999755859375\n",
-      "InVars: [<Var in1_a>, <Var in1_e>]\n",
-      "OutVars:[<Var mult_out1_a>, <Var mult_out1_e>]\n",
-      "A: true\n",
-      "G: -0.012500000000000011*in1_a + -0.175*in1_e + 1.0*mult_out1_e <= 0.015380859375, 1.0*mult_out1_a <= 0.1845703125, -0.1875*in1_a + 1.0*mult_out1_a <= 0.0\n",
-      "InVars: [<Var in1_a>, <Var in1_e>, <Var in2_a>, <Var in2_e>, <Var in3_a>, <Var in3_e>]\n",
-      "OutVars:[<Var add_out2_a>, <Var add_out2_e>]\n",
-      "A: true\n",
-      "G: 1*add_out2_e + -0.0125000000000000*in1_a + -0.175000000000000*in1_e + -0.00625000000000000*in2_a + -0.587500000000000*in2_e + -0.0125000000000000*in3_a + -0.175000000000000*in3_e <= 0.0461425781250000, 1*add_out2_a <= 0.953613281250000\n",
-      "InVars: []\n",
-      "OutVars:[<Var in1_a>, <Var in1_e>]\n",
-      "A: true\n",
-      "G: 1*in1_a <= 0.984375, -1*in1_a <= 0.0, 1*in1_e <= 0.0, -1*in1_e <= 0.0\n",
-      "InVars: []\n",
-      "OutVars:[<Var in2_a>, <Var in2_e>]\n",
-      "A: true\n",
-      "G: 1*in2_a <= 0.984375, -1*in2_a <= 0.0, 1*in2_e <= 0.0, -1*in2_e <= 0.0\n",
-      "InVars: []\n",
-      "OutVars:[<Var in3_a>, <Var in3_e>]\n",
-      "A: true\n",
-      "G: 1*in3_a <= 0.984375, -1*in3_a <= 0.0, 1*in3_e <= 0.0, -1*in3_e <= 0.0\n",
-      "InVars: [<Var in2_a>, <Var in2_e>, <Var in3_a>, <Var in3_e>]\n",
-      "OutVars:[<Var add_out2_a>, <Var add_out2_e>]\n",
-      "A: true\n",
-      "G: 1*add_out2_e + -0.00625000000000000*in2_a + -0.587500000000000*in2_e + -0.0125000000000000*in3_a + -0.175000000000000*in3_e <= 0.0584472656249999, 1*add_out2_a <= 0.953613281250000\n",
-      "InVars: []\n",
-      "OutVars:[<Var add_out2_a>, <Var add_out2_e>]\n",
-      "A: true\n",
-      "G: 1*add_out2_e <= 0.0769042968749998, 1*add_out2_a <= 0.953613281250000\n"
-     ]
-    }
-   ],
-   "source": [
-    "in1 = PortWordLength(n=6, p=0, e=0, name=\"in1\")\n",
-    "in2 = PortWordLength(n=6, p=0, e=0, name=\"in2\")\n",
-    "in3 = PortWordLength(n=6, p=0, e=0, name=\"in3\")\n",
-    "const1 = PortWordLength(n=6, p=0, name=\"const1\")\n",
-    "const2 = PortWordLength(n=6, p=0, name=\"const2\")\n",
-    "const3 = PortWordLength(n=6, p=0, name=\"const3\")\n",
-    "mult_out1 = PortWordLength(n=6, p=0, name=\"mult_out1\")\n",
-    "mult_out2 = PortWordLength(n=6, p=0, name=\"mult_out2\")\n",
-    "mult_out3 = PortWordLength(n=6, p=0, name=\"mult_out3\")\n",
-    "add_out1 = PortWordLength(n=6, p=0, name=\"add_out1\")\n",
-    "add_out2 = PortWordLength(n=6, p=0, name=\"add_out2\")\n",
-    "\n",
-    "const1.set_value(float_to_bin(0.2, const1))\n",
-    "const2.set_value(float_to_bin(0.6, const2))\n",
-    "const3.set_value(float_to_bin(0.2, const3))\n",
-    "const1.set_e(0.2 - const1.value_num())\n",
-    "const2.set_e(0.6 - const2.value_num())\n",
-    "const3.set_e(0.2 - const3.value_num())\n",
-    "\n",
-    "c1 = form_contract_mult_const(in_port1=in1, in_port_const=const1, out_port=mult_out1)\n",
-    "c2 = form_contract_mult_const(in_port1=in2, in_port_const=const2, out_port=mult_out2)\n",
-    "c3 = form_contract_mult_const(in_port1=in3, in_port_const=const3, out_port=mult_out3)\n",
-    "\n",
-    "ci1 = form_contract_input(in_port=in1)\n",
-    "ci2 = form_contract_input(in_port=in2)\n",
-    "ci3 = form_contract_input(in_port=in3)\n",
-    "\n",
-    "contract1 = readContract(c1)\n",
-    "contract2 = readContract(c2)\n",
-    "contract3 = readContract(c3)\n",
-    "print(str(contract1))\n",
-    "contract_i1 = readContract(ci1)\n",
-    "contract_i2 = readContract(ci2)\n",
-    "contract_i3 = readContract(ci3)\n",
-    "# print(str(contract1))\n",
-    "# print(str(contract2))\n",
-    "# print(str(contract3))\n",
-    "c4 = form_contract_add(in_port1 = mult_out1, in_port2=mult_out2, out_port=add_out1)\n",
-    "c5 = form_contract_add(in_port1 = add_out1, in_port2=mult_out3, out_port=add_out2)\n",
-    "contract4 = readContract(c4)\n",
-    "contract5 = readContract(c5)\n",
-    "# print(str(contract4))\n",
-    "# print(str(contract5))\n",
-    "contract_system = contract1.compose(contract2)\n",
-    "contract_system = contract_system.compose(contract3)\n",
-    "contract_system = contract_system.compose(contract4)\n",
-    "contract_system = contract_system.compose(contract5)\n",
-    "print(str(contract_system))\n",
-    "contract_system = contract_i1.compose(contract_system)\n",
-    "print(str(contract_i1))\n",
-    "print(str(contract_i2))\n",
-    "print(str(contract_i3))\n",
-    "print(str(contract_system))\n",
-    "contract_system = contract_i2.compose(contract_system)\n",
-    "contract_system = contract_i3.compose(contract_system)\n",
-    "print(str(contract_system))\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "6a546be1",
-   "metadata": {},
-   "source": [
-    "The result shows that we can get an upper bound on the error of the output `add_out_e` is bounded by about 0.07690.\n",
-    "And this is the extreme case when the input is the following case:\n",
-    "\n",
-    "Note that this is always an upper bound to the actual errors as we abstract the calculation and considers the worse case truncation error for each operation.\n",
-    "\n",
-    "If the obtained error is smaller than the specification, we can rest assure that the design already satisfy the goal and we can either submit the design or change some wordlength and see if we can use smaller wordlength to achieve the same specification.\n",
-    "\n",
-    "If the obtained error is larger than the specification, the level of abstraction is not sufficient to prove the correctness of the design.\n",
-    "Refinement of the error model or extensive verfication is therefore needed to verify the design.\n",
-    " "
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "cd0558b3",
-   "metadata": {},
-   "source": [
-    "To see the actual errors for this specific case, we can enumerate all input sequence.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "id": "4e0031d7",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "for i1 in range(0, 2**6):\n",
-    "    in1_a = float_to_bin(i1/ 2**6, in1)\n",
-    "    in1.set_value(in1_a)\n",
-    "    for i2 in range(0, 2**6):\n",
-    "        in2_a = float_to_bin(i2/ 2**6, in2)\n",
-    "        in2.set_value(in2_a)\n",
-    "        for i3 in range(0, 2**6):\n",
-    "            in3_a = float_to_bin(i3/2**6, in3)\n",
-    "            in3.set_value(in3_a)\n",
-    "\n",
-    "\n",
-    "            in1.value_num() * const1.value_num()\n",
-    "            in2.value_num() * const2.value_num()\n",
-    "            in3.value_num() * const3.value_num()\n",
-    "\n",
-    "    \n",
-    "            \n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "29ddbff5",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'0b111110000001'"
-      ]
-     },
-     "execution_count": 27,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "bin(int(0.984375*2**6) * int(0.984375*2**6))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "id": "f82bde73",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "InVars: []\n",
-      "OutVars:[<Var p3_e>]\n",
-      "A: true\n",
-      "G: 1*p3_e <= 10.1000000000000\n"
-     ]
-    }
-   ],
-   "source": [
-    "# exp\n",
-    "c1 = {'input_vars': [], \n",
-    "'output_vars': ['p2_a', 't2'], \n",
-    "'assumptions': [], \n",
-    "'guarantees': [ {'coefficients': {'p2_a': 1}, 'constant': 0.1}, \n",
-    "                {'coefficients': {'p2_a': -1}, 'constant': 0.0},\n",
-    "                {'coefficients': {'t2': 1}, 'constant': 0.1}, \n",
-    "                {'coefficients': {'t2': -1}, 'constant': 0.0}\n",
-    "              ]}\n",
-    "\n",
-    "c2 = {'input_vars': ['p2_a', 't2'], \n",
-    "'output_vars': ['p3_e'], \n",
-    "'assumptions': [], \n",
-    "'guarantees': [{'coefficients': {'p3_e': 1, 'p2_a': 1, 't2':-1}, 'constant': 10}]}\n",
-    "\n",
-    "contract1 = readContract(c1)\n",
-    "contract2 = readContract(c2)\n",
-    "system = contract1.compose(contract2)\n",
-    "print(str(system))\n",
-    "\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "9e4c7c7a",
-   "metadata": {},
-   "source": [
-    "#### Example 3: Fast-Fourier Transform (FFT) Cooley-Tucker Algorthm\n",
-    "\n",
-    "We applied the contract on the FFT Cooley-Tucker Algorithm of the Radix 2 decimation-in-time form.\n",
-    "The following function implements the eight-point FFT."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "id": "a120182a",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[1.+0.j 1.+0.j 1.+0.j 1.+0.j 1.+0.j 1.+0.j 1.+0.j 2.+0.j]\n",
-      "[ 9.00000000e+00+0.j          7.07106781e-01+0.70710678j\n",
-      " -6.12323400e-17+1.j         -7.07106781e-01+0.70710678j\n",
-      " -1.00000000e+00+0.j         -7.07106781e-01-0.70710678j\n",
-      "  6.12323400e-17-1.j          7.07106781e-01-0.70710678j]\n",
-      "[ 9.        +0.j          0.70710678+0.70710678j  0.        +1.j\n",
-      " -0.70710678+0.70710678j -1.        +0.j         -0.70710678-0.70710678j\n",
-      "  0.        -1.j          0.70710678-0.70710678j]\n",
-      "[1.+0.j 1.+0.j]\n"
-     ]
-    }
-   ],
-   "source": [
-    "import numpy as np\n",
-    "\n",
-    "def fft_8points(x):\n",
-    "\n",
-    "    s1 = np.zeros(x.shape, dtype=complex)\n",
-    "    W0 = np.exp(-2j * np.pi/8 * 0)\n",
-    "    W1 = np.exp(-2j * np.pi/8 * 1)\n",
-    "    W2 = np.exp(-2j * np.pi/8 * 2)\n",
-    "    W3 = np.exp(-2j * np.pi/8 * 3)\n",
-    "    #print(W0, W1, W2, W3)\n",
-    "\n",
-    "    s1[0] = x[0] + W0 * x[4]\n",
-    "    s1[1] = x[0] - W0 * x[4]\n",
-    "    s1[2] = x[2] + W0 * x[6]\n",
-    "    s1[3] = x[2] - W0 * x[6]\n",
-    "    s1[4] = x[1] + W0 * x[5]\n",
-    "    s1[5] = x[1] - W0 * x[5]\n",
-    "    s1[6] = x[3] + W0 * x[7]\n",
-    "    s1[7] = x[3] - W0 * x[7]\n",
-    "\n",
-    "    s2 = np.zeros(x.shape, dtype=complex)\n",
-    "    s2[0] = s1[0] + W0 * s1[2]\n",
-    "    s2[2] = s1[0] - W0 * s1[2]\n",
-    "    s2[1] = s1[1] + W2 * s1[3]\n",
-    "    s2[3] = s1[1] - W2 * s1[3]\n",
-    "    s2[4] = s1[4] + W0 * s1[6]\n",
-    "    s2[6] = s1[4] - W0 * s1[6]\n",
-    "    s2[5] = s1[5] + W2 * s1[7]\n",
-    "    s2[7] = s1[5] - W2 * s1[7]\n",
-    "\n",
-    "    s3 = np.zeros(x.shape, dtype=complex)\n",
-    "    s3[0] = s2[0] + W0 * s2[4]\n",
-    "    s3[4] = s2[0] - W0 * s2[4]\n",
-    "    s3[1] = s2[1] + W1 * s2[5]\n",
-    "    s3[5] = s2[1] - W1 * s2[5]\n",
-    "    s3[2] = s2[2] + W2 * s2[6]\n",
-    "    s3[6] = s2[2] - W2 * s2[6]\n",
-    "    s3[3] = s2[3] + W3 * s2[7]\n",
-    "    s3[7] = s2[3] - W3 * s2[7]\n",
-    "\n",
-    "    return s3\n",
-    "\n",
-    "t = np.arange(8)\n",
-    "#x = np.sin(t * 2 * np.pi / 8)\n",
-    "\n",
-    "x = np.array([1,1,1,1,1,1,1,2], dtype=complex)\n",
-    "print(x)\n",
-    "print(fft_8points(x=x))\n",
-    "print(np.fft.fft(x))\n",
-    "\n",
-    "x = np.array([1,0], dtype=complex)\n",
-    "print(np.fft.fft(x))\n",
-    "\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "3fbe38d7",
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "AssertionError",
-     "evalue": "",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
-      "Cell \u001b[0;32mIn[30], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m p4 \u001b[39m=\u001b[39m PortWordLength(n\u001b[39m=\u001b[39m\u001b[39m8\u001b[39m, p\u001b[39m=\u001b[39m\u001b[39m3\u001b[39m, name\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mp4\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m----> 2\u001b[0m p5 \u001b[39m=\u001b[39m PortWordLength(n\u001b[39m=\u001b[39;49m\u001b[39m8\u001b[39;49m, p\u001b[39m=\u001b[39;49m\u001b[39m3\u001b[39;49m, e\u001b[39m=\u001b[39;49m\u001b[39m0.03\u001b[39;49m, value\u001b[39m=\u001b[39;49m\u001b[39m\"\u001b[39;49m\u001b[39m11011\u001b[39;49m\u001b[39m\"\u001b[39;49m, name\u001b[39m=\u001b[39;49m\u001b[39m\"\u001b[39;49m\u001b[39mp5\u001b[39;49m\u001b[39m\"\u001b[39;49m) \u001b[39m# const\u001b[39;00m\n\u001b[1;32m      3\u001b[0m p6 \u001b[39m=\u001b[39m PortWordLength(n\u001b[39m=\u001b[39m\u001b[39m8\u001b[39m, p\u001b[39m=\u001b[39m\u001b[39m3\u001b[39m, name\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mp6\u001b[39m\u001b[39m\"\u001b[39m)\n",
-      "File \u001b[0;32m~/shengjungyu/Research/UC_Berkeley/Research/DesignSpecification/Contract/Tools/Contract/gear/case_studies/digital_signal_processing/tool.py:14\u001b[0m, in \u001b[0;36mPortWordLength.__init__\u001b[0;34m(self, n, p, e, a, name, value)\u001b[0m\n\u001b[1;32m     12\u001b[0m \u001b[39mif\u001b[39;00m value \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m     13\u001b[0m     \u001b[39massert\u001b[39;00m(\u001b[39misinstance\u001b[39m(value, \u001b[39mstr\u001b[39m))\n\u001b[0;32m---> 14\u001b[0m     \u001b[39massert\u001b[39;00m(\u001b[39mlen\u001b[39m(value) \u001b[39m==\u001b[39m n)\n\u001b[1;32m     15\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_value \u001b[39m=\u001b[39m value\n",
-      "\u001b[0;31mAssertionError\u001b[0m: "
-     ]
-    }
-   ],
-   "source": [
-    "p4 = PortWordLength(n=8, p=3, name=\"p4\")\n",
-    "p5 = PortWordLength(n=8, p=3, e=0.03, value=\"11011\", name=\"p5\") # const\n",
-    "p6 = PortWordLength(n=8, p=3, name=\"p6\")"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "2ca6a125",
-   "metadata": {},
-   "source": [
-    "### Problem with Composition Order\n",
-    "The order matters and create too restrictive bounds."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "id": "992aafd0",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Contract Sys:\n",
-      "InVars: []\n",
-      "OutVars:[<Var p5_a>, <Var p5_e>]\n",
-      "A: true\n",
-      "G: 1*p5_a <= 3.00000000000000, 1*p5_e <= 0.187500000000000\n",
-      "InVars: []\n",
-      "OutVars:[<Var p5_a>, <Var p5_e>]\n",
-      "A: true\n",
-      "G: 1*p5_a <= 3.00000000000000, 1*p5_e <= 0.187500000000000\n"
-     ]
-    }
-   ],
-   "source": [
-    "def create_example1_by_p3(p3_n, p3_p):\n",
-    "    p1 = PortWordLength(n=5, p=2, name = \"p1\")\n",
-    "    p2 = PortWordLength(n=5, p=3, name = \"p2\")\n",
-    "    p3 = PortWordLength(n=p3_n, p=p3_p, name = \"p3\")\n",
-    "    c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)\n",
-    "\n",
-    "    p4 = PortWordLength(n=7, p=3, name = \"p4\")\n",
-    "    p5 = PortWordLength(n=6, p=3, name = \"p5\")\n",
-    "    c2 = form_contract_add(in_port1=p3, in_port2=p4, out_port=p5)\n",
-    "\n",
-    "    p1.set_value(\"10000\")\n",
-    "    p2.set_value(\"00001\")\n",
-    "    p4.set_value(\"0001100\")\n",
-    "\n",
-    "    contract1 = readContract(c1)\n",
-    "    contract2 = readContract(c2)\n",
-    "    #print(\"Contract 1:\\n\" + str(contract1))\n",
-    "    #print(\"Contract 2:\\n\" + str(contract2))\n",
-    "    return contract1, contract2, p1, p2, p3, p4, p5\n",
-    "\n",
-    "def compose_order1(contract1, contract2, p1, p2, p3, p4, p5):\n",
-    "    c_p1 = form_contract_input(in_port=p1)\n",
-    "    c_p2 = form_contract_input(in_port=p2)\n",
-    "    c_p4 = form_contract_input(in_port=p4)\n",
-    "\n",
-    "    contract_p1 = readContract(c_p1)\n",
-    "    contract_p2 = readContract(c_p2)\n",
-    "    contract_p4 = readContract(c_p4)\n",
-    "    \n",
-    "    try: \n",
-    "        contract_sys = contract_p1.compose(contract1)\n",
-    "        contract_sys = contract_p2.compose(contract_sys)\n",
-    "        contract_sys = contract_sys.compose(contract2)\n",
-    "        contract_sys = contract_p4.compose(contract_sys)\n",
-    "        print(\"Contract Sys:\\n\" + str(contract_sys))\n",
-    "        return contract_sys\n",
-    "    except ValueError as e:\n",
-    "        print(\"Composition Error\")\n",
-    "        print(e)\n",
-    "        raise ValueError(\"Composition Fails\")\n",
-    "\n",
-    "def compose_order2(contract1, contract2, p1, p2, p3, p4, p5):\n",
-    "    c_p1 = form_contract_input(in_port=p1)\n",
-    "    c_p2 = form_contract_input(in_port=p2)\n",
-    "    c_p4 = form_contract_input(in_port=p4)\n",
-    "\n",
-    "    contract_p1 = readContract(c_p1)\n",
-    "    contract_p2 = readContract(c_p2)\n",
-    "    contract_p4 = readContract(c_p4)\n",
-    "    \n",
-    "    try: \n",
-    "        contract_sys = contract1.compose(contract2)\n",
-    "        contract_sys = contract_p2.compose(contract_sys)\n",
-    "        contract_sys = contract_sys.compose(contract2)\n",
-    "        contract_sys = contract_p4.compose(contract_sys)\n",
-    "        print(\"Contract Sys:\\n\" + str(contract_sys))\n",
-    "        return contract_sys\n",
-    "    except ValueError as e:\n",
-    "        print(\"Composition Error\")\n",
-    "        print(e)\n",
-    "        raise ValueError(\"Composition Fails\")\n",
-    "\n",
-    "contract1, contract2, p1, p2, p3, p4, p5 = create_example1_by_p3(p3_n=5, p3_p=3)\n",
-    "contract_sys = compose_order1(contract1, contract2, p1, p2, p3, p4, p5)\n",
-    "print(str(contract_sys))"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "EE227cfinal",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.10.4 | packaged by conda-forge | (main, Mar 24 2022, 17:45:10) [Clang 12.0.1 ]"
-  },
-  "vscode": {
-   "interpreter": {
-    "hash": "aed908b208154c5d14a5b26ec5e9587107b2c07b4bd6e6f91c00253c94cfc0d3"
-   }
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/case_studies/digital_signal_processing/figures/digital_data_path_example.png b/case_studies/digital_signal_processing/figures/digital_data_path_example.png
deleted file mode 100644
index 5c4bf7d2..00000000
Binary files a/case_studies/digital_signal_processing/figures/digital_data_path_example.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/figures/digital_filter_flow.png b/case_studies/digital_signal_processing/figures/digital_filter_flow.png
deleted file mode 100644
index 58472be4..00000000
Binary files a/case_studies/digital_signal_processing/figures/digital_filter_flow.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/figures/example1.png b/case_studies/digital_signal_processing/figures/example1.png
deleted file mode 100644
index faefac93..00000000
Binary files a/case_studies/digital_signal_processing/figures/example1.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/figures/type_of_error.png b/case_studies/digital_signal_processing/figures/type_of_error.png
deleted file mode 100644
index 6d6c20c8..00000000
Binary files a/case_studies/digital_signal_processing/figures/type_of_error.png and /dev/null differ
diff --git a/case_studies/digital_signal_processing/jupyter_debug.py b/case_studies/digital_signal_processing/jupyter_debug.py
deleted file mode 100644
index 0931c452..00000000
--- a/case_studies/digital_signal_processing/jupyter_debug.py
+++ /dev/null
@@ -1,211 +0,0 @@
-from gear.terms.polyhedra.loaders import readContract, writeContract
-from tool import *
-import numpy as np
-
-def truncation_error_same_position(pi, po):
-    assert(pi.p == po.p)
-    if pi.n > po.n:
-        return 2 ** po.p * (2 ** (-po.n) - 2 ** (-pi.n))
-    else :
-        return 0
-
-def truncate(pi, po):
-    # separate the bits into two parts, before binary point and after binary point
-    bits_before_point = pi.value[:pi.p]
-    bits_after_point = pi.value[pi.p:]
-    
-    # truncate or appending 0 
-    if po.p >= pi.p:
-        bits_before_point = ("0" * (po.p - pi.p)) + bits_before_point
-    else:
-        bits_before_point = bits_before_point[pi.p - po.p:]
-
-    if po.n - po.p >= pi.n - pi.p:
-        bits_after_point = bits_after_point + "0" * (po.n - po.p - pi.n + pi.p)
-    else:
-        bits_after_point = bits_after_point[:(po.n - po.p)]
-
-    # combine the both parts
-    ret = bits_before_point + bits_after_point
-    po.set_value(value = ret)
-    return po.value
-
-def truncation_error(pi, po):
-    # remove uneccesary most significant bits
-    # Remider: The assumption must hold!!
-    pi_adjusted = PortWordLength(n=pi.n - pi.p + po.p, p=po.p)
-    # return the truncation error as in case 1
-    return truncation_error_same_position(pi=pi_adjusted, po=po)
-
-def get_assumption_value(pi, po):
-    if pi.p > po.p:
-        return 2 ** po.p
-    else:
-        return float("inf") # no additional assumption needed
-
-def check_assumption_value(pi, po):
-    assumption_value = get_assumption_value(pi, po)
-    print(f"Assumption: {pi.name} < {assumption_value}")
-    if not pi.value_num() < assumption_value:
-        print("Assumption failed, truncation of MSB occurs")
-        return False
-    else:
-        print(f"Assumption Satisfied ({pi.name} = {pi.value_num()} < {assumption_value})")
-        return True
-        
-def compute_required_word_length_add(in1: PortWordLength, in2:PortWordLength) -> PortWordLength:
-    new_n = max(in1.n, in2.n - in2.p + in1.p) - min(0, in1.p - in2.p) + 1
-    new_p = max(in1.p, in2.p) + 1
-    return PortWordLength(n=new_n, p=new_p)
-
-def error_truncation_add(p1, p2, po):
-    print(f"Input {p1.name}: (n={p1.n}, p={p1.p})")
-    print(f"Input {p2.name}: (n={p2.n}, p={p2.p})")
-    p_ideal = compute_required_word_length_add(in1=p1, in2=p2)
-    print(f"Ideal Output: (n={p_ideal.n}, p={p_ideal.p})")
-    print(f"Actual Output ({po.name}): (n={po.n}, p={po.p})")
-
-    assumption_value = get_assumption_value(pi=p_ideal, po=po)
-    print(f"Assumption: {p1.name} + {p2.name} < {assumption_value}")
-    return truncation_error(pi=p_ideal, po=po)
-    
-def compute_required_word_length_mult(in1: PortWordLength, in2:PortWordLength) -> PortWordLength:
-    new_n = in1.n + in2.n
-    new_p = in1.p + in2.p
-    return PortWordLength(n=new_n, p=new_p)
-
-def error_truncation_mult(p1, p2, po):
-    print(f"Input {p1.name}: (n={p1.n}, p={p1.p})")
-    print(f"Input {p2.name}: (n={p2.n}, p={p2.p})")
-    p_ideal = compute_required_word_length_mult(in1=p1, in2=p2)
-    print(f"Ideal Output: (n={p_ideal.n}, p={p_ideal.p})")
-    print(f"Actual Output ({po.name}): (n={po.n}, p={po.p})")
-
-    assumption_value = get_assumption_value(pi=p_ideal, po=po)
-    print(f"Assumption: {p1.name} * {p2.name} < {assumption_value}")
-    return truncation_error(pi=p_ideal, po=po)
-
-def error_propagation_add(p1, p2, po):
-    return p1.e + p2.e
-
-def get_actual_possible_value(in_port: PortWordLength):
-    return (1 - (2 ** -in_port.n) )* 2 ** in_port.p
-
-def error_propagation_mult(p1, p2, po):
-    return p1.a * p2.e + p2.a * p1.e + p1.e * p2.e
-
-
-
-def form_contract_add(in_port1, in_port2, out_port):
-    ret_contract = {}
-    # define input/output vars
-    ret_contract["input_vars"] = [f"{in_port1.name}_a", f"{in_port1.name}_e",
-                                 f"{in_port2.name}_a", f"{in_port2.name}_e"]
-    ret_contract["output_vars"] = [f"{out_port.name}_a", f"{out_port.name}_e"]
-    # get assumption
-    ideal_out_port = compute_required_word_length_add(in1=in_port1, in2=in_port2)
-    assumption_value = get_assumption_value(pi=ideal_out_port, po=out_port)
-    # write assumption in the contract
-    if assumption_value is not float("inf"):
-        ret_contract["assumptions"] =  [{"coefficients":{f"{in_port1.name}_a":1, f"{in_port2.name}_a":1},
-                                    "constant":assumption_value}]
-    else:
-        ret_contract["assumptions"] = []
-    # get guarantee
-    e_t = truncation_error(pi=ideal_out_port, po=out_port)
-
-    # write guarantee in the contract, note the propagation is encoded in the polyhedral constraints
-    ret_contract["guarantees"] =  [{"coefficients":{f"{in_port1.name}_e":-1, f"{in_port2.name}_e":-1, f"{out_port.name}_e": 1},
-                                "constant":e_t},
-                                {"coefficients":{f"{out_port.name}_a": 1}, "constant":out_port.a},
-                                {"coefficients":{f"{out_port.name}_a": 1}, "constant":in_port1.a + in_port2.a},
-                                {"coefficients":{f"{out_port.name}_a": 1, f"{in_port1.name}_a": -1, f"{in_port2.name}_a": -1}, "constant":0}
-                                ]
-    return ret_contract
-
-
-def form_contract_mult_const(in_port1, in_port_const, out_port):
-    ret_contract = {}
-    # define input/output vars
-    ret_contract["input_vars"] = [f"{in_port1.name}_a", f"{in_port1.name}_e"]
-    ret_contract["output_vars"] = [f"{out_port.name}_a", f"{out_port.name}_e"]
-    # get assumption
-    ideal_out_port = compute_required_word_length_add(in1=in_port1, in2=in_port_const)
-    assumption_value = get_assumption_value(pi=ideal_out_port, po=out_port)
-    # write assumption in the contract
-    if assumption_value is not float("inf"):
-        ret_contract["assumptions"] =  [{"coefficients":{f"{in_port1.name}_a": in_port_const.a},
-                                    "constant":assumption_value}]
-    else:
-        ret_contract["assumptions"] = []
-    # get guarantee
-    e_t = truncation_error(pi=ideal_out_port, po=out_port)
-
-    # write guarantee in the contract, note the propagation is encoded in the polyhedral constraints
-    ret_contract["guarantees"] =  [{"coefficients":
-                                    {   f"{in_port1.name}_e":-in_port_const.a-in_port_const.e, 
-                                        f"{in_port1.name}_a":-in_port_const.e,
-                                        f"{out_port.name}_e": 1},
-                                    "constant":e_t},
-                                    {"coefficients":{f"{out_port.name}_a": 1}, "constant":out_port.a},
-                                    {"coefficients":{f"{out_port.name}_a": 1}, "constant":in_port_const.a * in_port1.a},
-                                    {"coefficients":{f"{out_port.name}_a": 1, f"{in_port1.name}_a": -in_port_const.a}, "constant":0}
-                                    ]
-    return ret_contract
-
-# p1 = PortWordLength(n=7, p=3, name="p1")
-# p2 = PortWordLength(n=5, p=2, name="p2")
-# p3 = PortWordLength(n=5, p=3, name="p3")
-# c1 = form_contract_add(in_port1=p1, in_port2=p2, out_port=p3)
-# print(c1)
-# contract1 = readContract(c1)
-# print(str(contract1))
-
-
-# p4 = PortWordLength(n=7, p=3, name="p4")
-# p5 = PortWordLength(n=5, p=2, e=0.03, value="11011", name="p5") # const
-# p6 = PortWordLength(n=5, p=3, name="p6")
-# c2 = form_contract_mult_const(in_port1=p4, in_port_const=p5, out_port=p6)
-# contract2 = readContract(c2)
-# print(str(contract2))
-
-# # exp
-
-# c1 = {'input_vars': ['p1_a', 'p1_e', 'p2_a', 'p2_e'], 'output_vars': ['p3_a', 'p3_e'], 'assumptions': [{'coefficients': {'p1_a': 1, 'p2_a': 1}, 'constant': 8}], 'guarantees': [{'coefficients': {'p1_e': -1, 'p2_e': -1, 'p3_e': 1}, 'constant': 0.125}, {'coefficients': {'p3_a': 1}, 'constant': 7.75}, {'coefficients': {'p3_a': 1}, 'constant': 11.625}, {'coefficients': {'p3_a': 1, 'p1_a': -1, 'p2_a': -1}, 'constant': 0}]}
-# c2 = {'input_vars': ['p3_a', 'p3_e', 'p4_a', 'p4_e'], 'output_vars': ['p5_a', 'p5_e'], 'assumptions': [{'coefficients': {'p3_a': 1, 'p4_a': 1}, 'constant': 8}], 'guarantees': [{'coefficients': {'p3_e': -1, 'p4_e': -1, 'p5_e': 1}, 'constant': 0.0625}, {'coefficients': {'p5_a': 1}, 'constant': 7.875}, {'coefficients': {'p5_a': 1}, 'constant': 15.6875}, {'coefficients': {'p5_a': 1, 'p3_a': -1, 'p4_a': -1}, 'constant': 0}]}
-# contract1 = readContract(c1)
-# contract2 = readContract(c2)
-# print(c1)
-# print(c2)
-# print("Contract 1:\n" + str(contract1))
-# print("Contract 2:\n" + str(contract2))
-# contract_sys = contract1.compose(contract2)
-# print("Contract Sys:\n" + str(contract_sys))
-# # exp
-# c1 = {'input_vars': [], 
-# 'output_vars': ['t1', 't2'], 
-# 'assumptions': [], 
-# 'guarantees': [ {'coefficients': {'t1': 1}, 'constant': 0.1}, 
-#                 {'coefficients': {'t1': -1}, 'constant': 0.},
-#                 {'coefficients': {'t2': 1}, 'constant': 0.2}, 
-#                 {'coefficients': {'t2': -1}, 'constant': 0.}
-#               ]}
-
-# c2 = {'input_vars': ['t1', 't2'], 
-# 'output_vars': ['o1'], 
-# 'assumptions': [], 
-# 'guarantees': [{'coefficients': {'o1': 1, 't1': 1, 't2':-1}, 'constant': 10}]}
-
-# contract1 = readContract(c1)
-# contract2 = readContract(c2)
-# system = contract1.compose(contract2)
-# print(str(system))
-
-
-c1 = {'input_vars': [], 'output_vars': ['p5_a', 'p5_e'], 'assumptions': [], 'guarantees': [{'coefficients': {'p5_a': 1}, 'constant': 6.0}, {'coefficients': {'p5_e': 1}, 'constant': 0.1}]}
-c2 = {'input_vars': [], 'output_vars': ['p1_a', 'p1_e'], 'assumptions': [], 'guarantees': [{'coefficients': {'p1_a': 1}, 'constant': 2.0}, {'coefficients': {'p1_a': -1}, 'constant': 0}, {'coefficients': {'p1_e': 1}, 'constant': 0}, {'coefficients': {'p1_e': -1}, 'constant': 0}]}
-contract1 = readContract(c1)
-contract2 = readContract(c2)
-print(contract1)
-print(contract2)
-print(contract1.quotient(contract2))
diff --git a/case_studies/digital_signal_processing/tool.py b/case_studies/digital_signal_processing/tool.py
index e76fe109..9778dee7 100644
--- a/case_studies/digital_signal_processing/tool.py
+++ b/case_studies/digital_signal_processing/tool.py
@@ -1,76 +1,189 @@
-import copy
+"""Tool.py
+
+The underlying data structure for dsp example
+"""
+
 import math
 
 DEBUG = True
 
+
 class PortWordLength(object):
-    def __init__(self, n: int = 0, p: int = 0, e: float = 0, a: float = None, name: str = "", value = None):
-        self._n = n # wordlength
-        self._p = p # wordlength
-        self._e = e # error known for this port
-        self._a = a # maximum possible value for this port
-        self._name = name # name of the port
+    """Class PortWordLength
+
+    Serve as the data structure for holding information of a port/signal
+    """
+
+    def __init__(self, n: int = 0, p: int = 0, e: float = 0, name: str = "", value: str | None = None):
+        """
+        Constructor of PortWordLength
+
+        Args:
+            n: number of bits of the port
+            p: number of bits of the integer parts
+            e: known error of the source to this port
+            name: name of the port
+            value: if not None, the port is a constant with value defined by it
+        """
+        self._n: int = n  # wordlength
+        self._p: int = p  # wordlength
+        self._e: float = e  # error known for this port
+        self._name: str = name  # name of the port
         if value is not None:
-            assert(isinstance(value, str))
-            assert(len(value) == n)
-        self._value = value # actual value for constant port.
+            assert isinstance(value, str)
+            assert len(value) == n
+        self._value: str | None = value  # actual value for constant port.
+
     @property
-    def n(self):
+    def n(self) -> int:
+        """
+        Obtain n from the port
+
+        Returns:
+            The number of bits for the port (n)
+        """
         return self._n
 
     @property
-    def a(self):
+    def a(self) -> float:
+        """
+        Obtain the maximum bounds of the port
+
+        Returns:
+            The maximum bounds of the port
+        """
         if self._value is None:
             return get_actual_possible_value(self)
-        else:
-            return self.value_num()
-    
+
+        return self.value_num()
+
     @property
-    def p(self):
+    def p(self) -> int:
+        """
+        Obtain p of the port
+
+        Returns:
+            The number of bits for the integer parts (p)
+        """
         return self._p
 
     @property
-    def e(self):
+    def e(self) -> float:
+        """Obtain error of the port
+
+        Returns:
+            The error bound of the source to the port
+        """
         return self._e
-    
+
+    @e.setter
+    def e(self, val: float) -> None:
+        """
+        Set known source error to the port
+
+        Args:
+            val: The known error source to the port
+        """
+        self._e = val
+
     @property
-    def name(self):
+    def name(self) -> str:
+        """
+        Obtain the name of the port
+
+        Returns:
+            The name of the port
+        """
         return self._name
 
+    @name.setter
+    def name(self, val: str) -> None:
+        """
+        Set name of the port
+
+        Args:
+            val: The name of the port
+        """
+        self._name = val
+
     @property
-    def value(self):
-        return self._value
+    def value(self) -> str | None:
+        """
+        Obtain the constant value set for the port
 
-    def set_value(self, value: str):
-        self._value = value
+        Returns:
+            The constant value set for the port
+        """
+        return self._value
 
-    def set_e(self, e: float):
-        self._e = e
+    @value.setter
+    def value(self, val: str) -> None:
+        """
+        Set constant value for the port
 
-    def set_a(self, a: float):
-        self._a = a
+        Args:
+            val: the constant value to be set for the port
+        """
+        self._value = val
 
-    def set_name(self, name: str):
-        self._name = name
+    def to_string(self) -> str:
+        """
+        Convert the port to string expression
 
-    def to_string(self):
+        Returns:
+            The string expression of the port
+        """
         return f"Port: {self.name}, (n, p) = ({self.n}, {self.p}), e = {self.e}, a = {self.a}"
 
-    def value_num(self):
-        return int(self.value, base=2) * 2 ** (- (self.n - self.p))
+    def value_num(self) -> float:
+        """
+        Convert string constant value to number
+
+        Returns:
+            The constant value of the port in floating number
+        """
+        return float(int(str(self.value), base=2) * 2 ** (-(self.n - self.p)))
+
 
+def port_add(in1: PortWordLength, in2: PortWordLength, out: PortWordLength) -> None:
+    """
+    Add the value of in1 and in2 and store it in out
 
-def port_add(in1, in2, out):
+    Args:
+        in1: PortWordLength object, one of the two the input ports
+        in2: PortWordLength object, one of the two the input ports
+        out: PortWordLength object, the output ports
+    """
     ideal_result = in1.value_num() + in2.value_num()
     actual_result = float_to_bin(ideal_result, out)
-    out.set_value(actual_result)
+    out.value = actual_result
 
-def port_mult(in1, in2, out):
+
+def port_mult(in1: PortWordLength, in2: PortWordLength, out: PortWordLength) -> None:
+    """
+    Multiply the value of in1 and in2 and store it in out
+
+    Args:
+        in1: PortWordLength object, one of the two the input ports
+        in2: PortWordLength object, one of the two the input ports
+        out: PortWordLength object, the output ports
+    """
     ideal_result = in1.value_num() * in2.value_num()
     actual_result = float_to_bin(ideal_result, out)
-    out.set_value(actual_result)
+    out.value = actual_result
+
+
+def float_to_bin(x: float, word_length: PortWordLength) -> str:
+    """
+    Convert float number to binary string
 
-def float_to_bin(x, word_length: PortWordLength):
+    Args:
+        x: the float number to be convert
+        word_length: PortWordLength, the port that will hold this float number.
+
+    Returns:
+        The string of the value holding the floating point
+    """
     frac_part, int_part = math.modf(x)
     p = word_length.p
     # get integer part
@@ -88,13 +201,22 @@ def float_to_bin(x, word_length: PortWordLength):
     while len(frac_str) != req_length:
         frac_part *= 2
         if frac_part >= 1:
-            frac_str += '1'
+            frac_str += "1"
             frac_part -= 1
         else:
-            frac_str += '0'
+            frac_str += "0"
 
     return bin_str + frac_str
 
 
-def get_actual_possible_value(in_port: PortWordLength):
-    return (1 - (2 ** -in_port.n) )* 2 ** in_port.p
\ No newline at end of file
+def get_actual_possible_value(in_port: PortWordLength) -> float:
+    """
+    Compute the maximum bound based on the port wordlength
+
+    Args:
+        in_port: PortWordLength, the port for computing maximum bound.
+
+    Returns:
+        The maximum bound given the word length
+    """
+    return float((1 - (2**-in_port.n)) * 2**in_port.p)
diff --git a/case_studies/evaluating_perception/case_study.ipynb b/case_studies/evaluating_perception/case_study.ipynb
index 8ad99ee2..c7baef1f 100644
--- a/case_studies/evaluating_perception/case_study.ipynb
+++ b/case_studies/evaluating_perception/case_study.ipynb
@@ -30,14 +30,12 @@
    ],
    "source": [
     "# Import libraries\n",
-    "from pacti.iocontract.utils import getVarlist\n",
-    "from PIL import Image\n",
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
-    "import pdb\n",
-    "from case_study import * \n",
-    "import random\n",
     "import pickle as pkl\n",
-    "import collections"
+    "\n",
+    "from case_study import *\n",
+    "from PIL import Image\n",
+    "\n",
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -56,15 +54,15 @@
    "outputs": [],
    "source": [
     "TILESIZE = 50\n",
-    "ORIENTATIONS = {'n': 270, 'e': 0, 's': 90,'w':180, 'ne':315, 'nw':225, 'se':45, 'sw':135}\n",
+    "ORIENTATIONS = {\"n\": 270, \"e\": 0, \"s\": 90, \"w\": 180, \"ne\": 315, \"nw\": 225, \"se\": 45, \"sw\": 135}\n",
     "START_CROSSWALK = -1\n",
     "END_CROSSWALK = 2\n",
     "CROSSWALK_V = 1\n",
     "CROSSWALK_LOCATIONS = dict()\n",
     "ped_loc = []\n",
-    "for i, num in enumerate(range(2*START_CROSSWALK,2*(END_CROSSWALK+1))):\n",
-    "    CROSSWALK_LOCATIONS.update({i: (num/2, CROSSWALK_V)})\n",
-    "    ped_loc.append(num/2)\n"
+    "for i, num in enumerate(range(2 * START_CROSSWALK, 2 * (END_CROSSWALK + 1))):\n",
+    "    CROSSWALK_LOCATIONS.update({i: (num / 2, CROSSWALK_V)})\n",
+    "    ped_loc.append(num / 2)"
    ]
   },
   {
@@ -94,23 +92,23 @@
     }
    ],
    "source": [
-    "class GridCar():\n",
+    "class GridCar:\n",
     "    def __init__(self):\n",
     "        self.xc_init = 0\n",
     "        self.vc_init = 0\n",
     "        self.ped = 1\n",
     "        self.N = 10\n",
     "        self.Vmax = 2\n",
-    "        self.xped = 10-CROSSWALK_V\n",
+    "        self.xped = 10 - CROSSWALK_V\n",
     "        self.main_dir = os.path.dirname(os.path.dirname(os.path.realpath(\"__file__\")))\n",
-    "        self.car_fig = self.main_dir + '/evaluating_perception/imglib/red_car.png'\n",
-    "        self.ped_fig = self.main_dir + '/evaluating_perception/imglib/pedestrian_img.png'\n",
+    "        self.car_fig = self.main_dir + \"/evaluating_perception/imglib/red_car.png\"\n",
+    "        self.ped_fig = self.main_dir + \"/evaluating_perception/imglib/pedestrian_img.png\"\n",
     "        self.car_data = [9, 0]\n",
     "        if self.ped == 1:\n",
     "            self.ped_data = [CROSSWALK_V, min(ped_loc)]\n",
     "        else:\n",
     "            self.ped_data = None\n",
-    "    \n",
+    "\n",
     "    def dynamics(self):\n",
     "        transitions = dict()\n",
     "        for x in range(0, self.N):\n",
@@ -119,7 +117,7 @@
     "                x_succ.append(x + v)\n",
     "            transitions[x] = x_succ\n",
     "        return transitions\n",
-    "    \n",
+    "\n",
     "    def plot_grid(self):\n",
     "        size = [1, self.N]\n",
     "        x_min = 0\n",
@@ -127,9 +125,9 @@
     "        y_min = 0\n",
     "        y_max = size[1] * TILESIZE\n",
     "        # x_min, x_max, y_min, y_max = get_map_corners(map)\n",
-    "        \n",
+    "\n",
     "        fig, ax = plt.subplots()\n",
-    "        ax.axis('equal')\n",
+    "        ax.axis(\"equal\")\n",
     "        ax.set_xlim(x_min, x_max)\n",
     "        ax.set_ylim(y_min, y_max)\n",
     "        ax.xaxis.set_visible(False)\n",
@@ -137,12 +135,14 @@
     "\n",
     "        # fill in the road regions\n",
     "        road_tiles = []\n",
-    "        width_tiles = np.arange(0,size[0]+1)*2*TILESIZE\n",
-    "        lanes_tiles = np.arange(0,size[1]+1)*TILESIZE\n",
-    "        \n",
-    "        for i in np.arange(0,size[0]):\n",
-    "            for k in np.arange(0,size[1]+1):\n",
-    "                tile = patches.Rectangle((width_tiles[i],lanes_tiles[k]),2*TILESIZE,TILESIZE,linewidth=1,facecolor='k', alpha=0.4)\n",
+    "        width_tiles = np.arange(0, size[0] + 1) * 2 * TILESIZE\n",
+    "        lanes_tiles = np.arange(0, size[1] + 1) * TILESIZE\n",
+    "\n",
+    "        for i in np.arange(0, size[0]):\n",
+    "            for k in np.arange(0, size[1] + 1):\n",
+    "                tile = patches.Rectangle(\n",
+    "                    (width_tiles[i], lanes_tiles[k]), 2 * TILESIZE, TILESIZE, linewidth=1, facecolor=\"k\", alpha=0.4\n",
+    "                )\n",
     "                road_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(road_tiles, match_original=True))\n",
     "\n",
@@ -150,76 +150,79 @@
     "        crosswalk_tiles = []\n",
     "        for item in CROSSWALK_LOCATIONS.keys():\n",
     "            if item % 2 == 0:\n",
-    "                color = 'silver'\n",
+    "                color = \"silver\"\n",
     "                alpha = 0.5\n",
     "            else:\n",
-    "                color = 'k'\n",
+    "                color = \"k\"\n",
     "                alpha = 0.5\n",
-    "            width = CROSSWALK_LOCATIONS[item][0]*TILESIZE\n",
-    "            lanes = CROSSWALK_LOCATIONS[item][1]*TILESIZE\n",
-    "            tile = patches.Rectangle((width,lanes),TILESIZE/2,TILESIZE,linewidth=1,facecolor=color, alpha=alpha)\n",
+    "            width = CROSSWALK_LOCATIONS[item][0] * TILESIZE\n",
+    "            lanes = CROSSWALK_LOCATIONS[item][1] * TILESIZE\n",
+    "            tile = patches.Rectangle((width, lanes), TILESIZE / 2, TILESIZE, linewidth=1, facecolor=color, alpha=alpha)\n",
     "            crosswalk_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(crosswalk_tiles, match_original=True))\n",
     "        # TODO: Add cross-walk tile names next to the cells\n",
     "        plt.gca().invert_yaxis()\n",
     "        return ax\n",
-    "    \n",
-    "    \n",
+    "\n",
     "    def plot_car(self, ax):\n",
     "        y_tile, speed = self.car_data\n",
-    "        theta_d = 270 # Rotate car to face north\n",
-    "        x = (1) * TILESIZE/2\n",
+    "        theta_d = 270  # Rotate car to face north\n",
+    "        x = (1) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        car_fig = Image.open(self.car_fig)\n",
     "        car_fig = car_fig.rotate(theta_d, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(car_fig, zorder=1, interpolation='bilinear', extent=[x+2, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            car_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 2, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    def plot_ped(self, ax):\n",
     "        y_tile, x_tile = self.ped_data\n",
-    "        x = (x_tile) * TILESIZE/2\n",
+    "        x = (x_tile) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        ped_fig = Image.open(self.ped_fig)\n",
     "        ped_fig = ped_fig.rotate(180, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(ped_fig, zorder=1, interpolation='bilinear', extent=[x+4, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            ped_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 4, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    # Function to calculate all system states:\n",
     "    def construct_transitions(self):\n",
     "        T = dict()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
     "                st = (xcar, vcar)\n",
     "                end_st = []\n",
     "                if xcar == self.N:\n",
     "                    end_st.append((xcar, vcar))\n",
     "                elif vcar == 0:\n",
-    "                    xcar_p = min(self.N, xcar+1)\n",
+    "                    xcar_p = min(self.N, xcar + 1)\n",
     "                    end_st.append((xcar, vcar))\n",
-    "                    end_st.append((xcar_p, vcar+1))\n",
-    "                    end_st.append((xcar, vcar+1))\n",
+    "                    end_st.append((xcar_p, vcar + 1))\n",
+    "                    end_st.append((xcar, vcar + 1))\n",
     "                else:\n",
-    "                    xcar_p = min(self.N, xcar+vcar)\n",
+    "                    xcar_p = min(self.N, xcar + vcar)\n",
     "                    end_st.append((xcar_p, vcar))\n",
-    "                    end_st.append((xcar_p, vcar-1))\n",
+    "                    end_st.append((xcar_p, vcar - 1))\n",
     "                    if vcar < Vhigh:\n",
-    "                        end_st.append((xcar_p, vcar+1))\n",
+    "                        end_st.append((xcar_p, vcar + 1))\n",
     "                T[st] = end_st\n",
     "        return T\n",
-    "    \n",
+    "\n",
     "    # System states for pedestrian\n",
     "    def system_states_example_ped(self):\n",
-    "        nS = self.N*(self.Vmax+1)\n",
-    "        state = lambda x,v: (self.Vmax+1)*(x-1) + v\n",
+    "        nS = self.N * (self.Vmax + 1)\n",
+    "        state = lambda x, v: (self.Vmax + 1) * (x - 1) + v\n",
     "        state_to_S = dict()\n",
     "        S = set()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
-    "                st = \"S\"+str(state(xcar, vcar))\n",
-    "                state_to_S[xcar,vcar] = st\n",
-    "                S|={st}\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
+    "                st = \"S\" + str(state(xcar, vcar))\n",
+    "                state_to_S[xcar, vcar] = st\n",
+    "                S |= {st}\n",
     "        T = self.construct_transitions()\n",
     "        return S, state_to_S, T\n",
     "\n",
@@ -267,7 +270,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "# ubounds_ped, lbounds_ped, points_ped, tpped_vals = derive_prob_bounds_lp(fn, true_env)\n",
     "# plot_probabilities_bounds(points_ped, tpped_vals, true_env, vmax, Ncar, ubounds_ped, lbounds_ped, rand =True)\n",
@@ -283,7 +286,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "# ubounds_obj, lbounds_obj, points_obj, tpobj_vals = derive_prob_bounds_lp(fn, true_env)\n",
     "# plot_probabilities_bounds(points_obj, tpobj_vals, true_env, vmax, Ncar, ubounds_obj, lbounds_obj, rand =True)\n",
@@ -299,7 +302,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "# ubounds_emp, lbounds_emp, points_emp, tpemp_vals = derive_prob_bounds_lp(fn, true_env)\n",
     "# plot_probabilities_bounds(points_emp, tpemp_vals, true_env, vmax, Ncar, ubounds_emp, lbounds_emp, rand =True)\n",
@@ -361,78 +364,67 @@
     "## Control contract for pedestrian class\n",
     "def control_contract_ped(lbounds_ped):\n",
     "    lb_m, lb_c = lbounds_ped\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_ped\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"tp_ped\": -1}, \"constant\": -0.6}],\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"tp_ped\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"tp_ped\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_obj(lbounds_obj):\n",
     "    lb_m, lb_c = lbounds_obj\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            #             {\"coefficients\":{\"tp_obj\":1},\n",
+    "            #             \"constant\":0.999},\n",
+    "            {\"coefficients\": {\"tp_obj\": -1}, \"constant\": -0.3}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "#             {\"coefficients\":{\"tp_obj\":1},\n",
-    "#             \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_obj\":-1},\n",
-    "            \"constant\":-0.3}\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"tp_obj\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_obj\":-1, \"tp_obj\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_emp(lbounds_emp):\n",
     "    lb_m, lb_c = lbounds_emp\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            #             {\"coefficients\":{\"tp_emp\":1},\n",
+    "            #             \"constant\":0.999},\n",
+    "            {\"coefficients\": {\"tp_emp\": -1}, \"constant\": -0.6}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "#             {\"coefficients\":{\"tp_emp\":1},\n",
-    "#             \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_emp\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"tp_emp\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_emp\":-1, \"tp_emp\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cped = PolyhedralContract.from_dict(control_contract_ped(lbounds_ped))\n",
     "Cobj = PolyhedralContract.from_dict(control_contract_obj(lbounds_obj))\n",
     "Cemp = PolyhedralContract.from_dict(control_contract_emp(lbounds_emp))\n",
     "\n",
     "print(\"Controller Contract for pedestrian class:\\n\" + str(Cped) + \"\\n\")\n",
-    "print(\"Controller Contract for object class:\\n\" + str(Cobj)+ \"\\n\")\n",
-    "print(\"Controller Contract for empty class:\\n\" + str(Cemp)+ \"\\n\")\n"
+    "print(\"Controller Contract for object class:\\n\" + str(Cobj) + \"\\n\")\n",
+    "print(\"Controller Contract for empty class:\\n\" + str(Cemp) + \"\\n\")"
    ]
   },
   {
@@ -456,7 +448,7 @@
    ],
    "source": [
     "# Construct merger of contracts:\n",
-    "C_controller = Cped.merge(Cobj) \n",
+    "C_controller = Cped.merge(Cobj)\n",
     "C_controller = C_controller.merge(Cemp)\n",
     "\n",
     "print(\"Merged Controller Contract for all object classes:\\n\" + str(C_controller) + \"\\n\")"
@@ -485,34 +477,24 @@
     "## System contract: P >= 0.99\n",
     "def system_contract_dist():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"d\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"d\":-1}, \"constant\":0},\n",
-    "            {\"coefficients\":{\"d\":1}, \"constant\":10}\n",
+    "        \"input_vars\": [\"d\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"d\": -1}, \"constant\": 0}, {\"coefficients\": {\"d\": 1}, \"constant\": 10}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"d\": -0.099}, \"constant\": -0.99},\n",
+    "            #             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"d\": -0.08}, \"constant\": -0.8},\n",
+    "            #             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"d\": -0.095}, \"constant\": -0.95},\n",
+    "            #             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
+    "            #             \"constant\":1}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1, \"d\":-0.099},\n",
-    "            \"constant\":-0.99},\n",
-    "#             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1, \"d\":-0.08},\n",
-    "            \"constant\":-0.8},\n",
-    "#             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1, \"d\":-0.095},\n",
-    "            \"constant\":-0.95},\n",
-    "#             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
-    "#             \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys_dist = PolyhedralContract.from_dict(system_contract_dist())\n",
     "print(\"System Contract (distance bounds):\\n\" + str(Csys_dist) + \"\\n\")"
    ]
@@ -539,7 +521,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_dist = Csys_dist.quotient(C_controller)\n",
-    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist)+\"\\n\")"
+    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist) + \"\\n\")"
    ]
   },
   {
@@ -560,7 +542,7 @@
     }
    ],
    "source": [
-    "-1.61205249090165/-1.58012560150300"
+    "-1.61205249090165 / -1.58012560150300"
    ]
   },
   {
@@ -595,82 +577,67 @@
     "def control_contract_ped(ubounds_ped, lbounds_ped):\n",
     "    ub_m, ub_c = ubounds_ped\n",
     "    lb_m, lb_c = lbounds_ped\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_ped\": 1}, \"constant\": 0.999},\n",
+    "            {\"coefficients\": {\"tp_ped\": -1}, \"constant\": -0.6},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_ped\":1},\n",
-    "            \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_ped\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": 1, \"tp_ped\": -1 * ub_m}, \"constant\": ub_c},\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"tp_ped\": lb_m}, \"constant\": -1 * lb_c},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
-    "          \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"tp_ped\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_obj(ubounds_obj, lbounds_obj):\n",
     "    ub_m, ub_c = ubounds_obj\n",
     "    lb_m, lb_c = lbounds_obj\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_obj\", \"tp_ped\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_obj\", \"P_ped\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_obj\", \"tp_ped\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_obj\", \"P_ped\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_obj\": 1}, \"constant\": 0.999},\n",
+    "            {\"coefficients\": {\"tp_obj\": -1}, \"constant\": -0.3},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_obj\":1},\n",
-    "            \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_obj\":-1},\n",
-    "            \"constant\":-0.3}\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_obj\": 1, \"tp_obj\": -1 * ub_m}, \"constant\": ub_c},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"tp_obj\": lb_m}, \"constant\": -1 * lb_c},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
-    "          \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_obj\":-1, \"tp_obj\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_emp(ubounds_emp, lbounds_emp):\n",
     "    ub_m, ub_c = ubounds_emp\n",
     "    lb_m, lb_c = lbounds_emp\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_emp\",\"tp_obj\", \"tp_ped\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_emp\",\"P_obj\", \"P_ped\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_emp\", \"tp_obj\", \"tp_ped\"],\n",
+    "        \"output_vars\": [\"P_emp\", \"P_obj\", \"P_ped\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_emp\": 1}, \"constant\": 0.999},\n",
+    "            {\"coefficients\": {\"tp_emp\": -1}, \"constant\": -0.6},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_emp\":1},\n",
-    "            \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_emp\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_emp\": 1, \"tp_emp\": -1 * ub_m}, \"constant\": ub_c},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"tp_emp\": lb_m}, \"constant\": -1 * lb_c},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
-    "          \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_emp\":-1, \"tp_emp\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cped = PolyhedralContract.from_dict(control_contract_ped(ubounds_ped, lbounds_ped))\n",
     "Cobj = PolyhedralContract.from_dict(control_contract_obj(ubounds_obj, lbounds_obj))\n",
     "Cemp = PolyhedralContract.from_dict(control_contract_emp(ubounds_emp, lbounds_emp))\n",
     "\n",
     "print(\"Controller Contract for pedestrian class:\\n\" + str(Cped) + \"\\n\")\n",
-    "print(\"Controller Contract for object class:\\n\" + str(Cobj)+ \"\\n\")\n",
-    "print(\"Controller Contract for empty class:\\n\" + str(Cemp)+ \"\\n\")"
+    "print(\"Controller Contract for object class:\\n\" + str(Cobj) + \"\\n\")\n",
+    "print(\"Controller Contract for empty class:\\n\" + str(Cemp) + \"\\n\")"
    ]
   },
   {
@@ -689,7 +656,7 @@
    "outputs": [],
    "source": [
     "# Construct merger of contracts:\n",
-    "C_controller = Cped.compose(Cobj) \n",
+    "C_controller = Cped.compose(Cobj)\n",
     "C_controller = C_controller.compose(Cemp)\n",
     "\n",
     "print(\"Merged Controller Contract for all object classes:\\n\" + str(C_controller) + \"\\n\")"
@@ -713,34 +680,21 @@
     "## System contract: P >= 0.99 for each class\n",
     "def system_contract():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"x_e\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "        \"input_vars\": [\"x_e\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"x_e\": -1}, \"constant\": -1}, {\"coefficients\": {\"x_e\": 1}, \"constant\": 1}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1}, \"constant\": -0.99},\n",
+    "            {\"coefficients\": {\"P_ped\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1}, \"constant\": -0.80},\n",
+    "            {\"coefficients\": {\"P_obj\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1}, \"constant\": -0.95},\n",
+    "            {\"coefficients\": {\"P_emp\": 1}, \"constant\": 1},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"x_e\":-1}, \"constant\":-1},\n",
-    "            {\"coefficients\":{\"x_e\":1}, \"constant\":1}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P_ped\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1},\n",
-    "            \"constant\":-0.80},\n",
-    "            {\"coefficients\":{\"P_obj\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1},\n",
-    "            \"constant\":-0.95},\n",
-    "            {\"coefficients\":{\"P_emp\":1},\n",
-    "            \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys = PolyhedralContract.from_dict(system_contract())\n",
     "print(\"System Contract (numerical bounds):\\n\" + str(Csys) + \"\\n\")"
    ]
@@ -754,7 +708,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_numerical = Csys.quotient(C_controller)\n",
-    "print(\"Quotient Contract with numerical bounds on true positive rates:\\n\" + str(C_numerical)+\"\\n\")"
+    "print(\"Quotient Contract with numerical bounds on true positive rates:\\n\" + str(C_numerical) + \"\\n\")"
    ]
   },
   {
@@ -764,7 +718,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "-1.77988941044192/-1.78978009053245"
+    "-1.77988941044192 / -1.78978009053245"
    ]
   },
   {
@@ -785,34 +739,21 @@
     "## System contract: P >= 0.99\n",
     "def system_contract_dist():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"d\"\n",
+    "        \"input_vars\": [\"d\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"d\": -1}, \"constant\": 0}, {\"coefficients\": {\"d\": 1}, \"constant\": 10}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"d\": -0.099}, \"constant\": -0.99},\n",
+    "            {\"coefficients\": {\"P_ped\": 1, \"d\": 0.00}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"d\": -0.08}, \"constant\": -0.8},\n",
+    "            {\"coefficients\": {\"P_obj\": 1, \"d\": 0}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"d\": -0.095}, \"constant\": -0.95},\n",
+    "            {\"coefficients\": {\"P_emp\": 1, \"d\": 0.0}, \"constant\": 1},\n",
     "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"d\":-1}, \"constant\":0},\n",
-    "            {\"coefficients\":{\"d\":1}, \"constant\":10}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1, \"d\":-0.099},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1, \"d\":-0.08},\n",
-    "            \"constant\":-0.8},\n",
-    "            {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1, \"d\":-0.095},\n",
-    "            \"constant\":-0.95},\n",
-    "            {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
-    "            \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys_dist = PolyhedralContract.from_dict(system_contract_dist())\n",
     "print(\"System Contract (distance bounds):\\n\" + str(Csys_dist) + \"\\n\")"
    ]
@@ -826,7 +767,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_dist = Csys_dist.quotient(C_controller)\n",
-    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist)+\"\\n\")"
+    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist) + \"\\n\")"
    ]
   },
   {
@@ -836,7 +777,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "0.15030000000/0.200500000000000"
+    "0.15030000000 / 0.200500000000000"
    ]
   },
   {
diff --git a/case_studies/evaluating_perception/case_study.py b/case_studies/evaluating_perception/case_study.py
index 1070010b..9016d574 100644
--- a/case_studies/evaluating_perception/case_study.py
+++ b/case_studies/evaluating_perception/case_study.py
@@ -1,29 +1,31 @@
 # Import libraries
-from pacti.iocontract.utils import getVarlist
+import collections
 import pdb
-from utils import *
-import random
 import pickle as pkl
+import random
+
 import cvxpy as cp
-import collections
+from utils import *
+
 
 # Construct random confusion matrix
 def construct_CM(tp_ped, tp_obj, tp_emp):
     C = dict()
     C["ped", "ped"] = tp_ped
     coeff = random.random()
-    C["obj", "ped"] = coeff*(1-tp_ped)
-    C["empty", "ped"] = (1-coeff)*(1 - tp_ped)
+    C["obj", "ped"] = coeff * (1 - tp_ped)
+    C["empty", "ped"] = (1 - coeff) * (1 - tp_ped)
 
-    C["ped", "obj"] = 0.1*(1-tp_obj)
+    C["ped", "obj"] = 0.1 * (1 - tp_obj)
     C["obj", "obj"] = tp_obj
-    C["empty", "obj"] = 0.9*(1-tp_obj)
+    C["empty", "obj"] = 0.9 * (1 - tp_obj)
 
-    C["ped", "empty"] = 0.5*(1-tp_emp)
-    C["obj", "empty"] = 0.5*(1-tp_emp)
+    C["ped", "empty"] = 0.5 * (1 - tp_emp)
+    C["obj", "empty"] = 0.5 * (1 - tp_emp)
     C["empty", "empty"] = tp_emp
     return C
 
+
 def call_MC(K, K_backup, C, true_env, true_env_type, state_info, M):
     importlib.reload(Kped)
     importlib.reload(Kobj)
@@ -50,167 +52,177 @@ def call_MC(K, K_backup, C, true_env, true_env_type, state_info, M):
     start_state = state_info["start"]
     bad_states = state_info["bad"]
     good_state = state_info["good"]
-    MC = M.to_MC(start_state) # For setting initial conditions and assigning bad/good labels
+    MC = M.to_MC(start_state)  # For setting initial conditions and assigning bad/good labels
     print(M.M)
     return M
 
+
 def initialize_ped(vmax, MAX_V, true_env_type):
-    Ncar = int(MAX_V*(MAX_V+1)/2 + 2)
-    Vlow =  0
+    Ncar = int(MAX_V * (MAX_V + 1) / 2 + 2)
+    Vlow = 0
     Vhigh = vmax
-    x_vmax_stop = vmax*(vmax+1)/2 + 1
+    x_vmax_stop = vmax * (vmax + 1) / 2 + 1
     xcross_start = 2
     Nped = Ncar - xcross_start + 1
     if x_vmax_stop >= xcross_start:
         min_xped = int(x_vmax_stop + 1 - (xcross_start - 1))
     else:
         min_xped = 3
-    assert(min_xped > 0)
-    assert(min_xped<= Nped)
+    assert min_xped > 0
+    assert min_xped <= Nped
     if min_xped < Nped:
         xped = np.random.randint(min_xped, Nped)
     else:
         xped = int(min_xped)
     xped = int(min_xped)
     xcar_stop = xped + xcross_start - 2
-    assert(xcar_stop > 0)
-    state_f = lambda x,v: (Vhigh-Vlow+1)*(x-1)+v
+    assert xcar_stop > 0
+    state_f = lambda x, v: (Vhigh - Vlow + 1) * (x - 1) + v
     bad_states = set()
     good_state = set()
 
     def get_formula_states(xcar_stop):
         bst = set()
-        for vi in range(1,Vhigh+1):
+        for vi in range(1, Vhigh + 1):
             state = state_f(xcar_stop, vi)
-            bst |= {"S"+str(state)}
-        gst = {"S" + str(state_f(xcar_stop,0))}
-        bad = "" # Expression for bad states
-        good = "" # Expression for good states
+            bst |= {"S" + str(state)}
+        gst = {"S" + str(state_f(xcar_stop, 0))}
+        bad = ""  # Expression for bad states
+        good = ""  # Expression for good states
         for st in list(gst):
             if good == "":
-                good = good + "\"" + st+"\""
+                good = good + '"' + st + '"'
             else:
-                good = good + "|\""+st+"\""
+                good = good + '|"' + st + '"'
         for st in list(bst):
             if bad == "":
-                bad = bad + "\"" + st+"\""
+                bad = bad + '"' + st + '"'
             else:
-                bad = bad + "|\""+st+"\""
+                bad = bad + '|"' + st + '"'
         return good, bad, gst, bst
+
     good, bad, gst, bst = get_formula_states(xcar_stop)
     good_state |= gst
     bad_states |= bst
     pdb.set_trace()
-    phi1 = "!("+good+")" # This is for empty and obj states --> to keep driving
-    phi2 = "("+good+") | !("+bad # This is to stop if there is a ped
-    for xcar_ii in range(xcar_stop+1, Ncar+1):
-        good, bad, gst, bst = get_formula_states(xcar_ii) # We only want the bad states; ignore the good states output here
+    phi1 = "!(" + good + ")"  # This is for empty and obj states --> to keep driving
+    phi2 = "(" + good + ") | !(" + bad  # This is to stop if there is a ped
+    for xcar_ii in range(xcar_stop + 1, Ncar + 1):
+        good, bad, gst, bst = get_formula_states(
+            xcar_ii
+        )  # We only want the bad states; ignore the good states output here
         bad_states |= bst
         phi2 = phi2 + "|" + bad
     phi2 = phi2 + ")"
     if true_env_type == "ped":
-        formula = "P=?[G("+str(phi2)+")]" # Don't reach bad states and eventually reach good states
+        formula = "P=?[G(" + str(phi2) + ")]"  # Don't reach bad states and eventually reach good states
     else:
-        formula = "P=?[G("+str(phi1)+")]" # Don't reach the stop state at crosswalk
+        formula = "P=?[G(" + str(phi1) + ")]"  # Don't reach the stop state at crosswalk
     print(formula)
     return Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula
 
+
 # Checking initialization
 def new_initialize_ped(vmax, Ncar, true_env_type):
-    assert vmax*(vmax+1)/2 <= Ncar
-    Vlow =  0
+    assert vmax * (vmax + 1) / 2 <= Ncar
+    Vlow = 0
     Vhigh = vmax
-    x_vmax_stop = vmax*(vmax+1)/2 + 1
+    x_vmax_stop = vmax * (vmax + 1) / 2 + 1
     xped = Ncar - 1
     xcar_stop = xped - 1
-    assert(xcar_stop > 0)
-    state_f = lambda x,v: (Vhigh-Vlow+1)*(x-1)+v
+    assert xcar_stop > 0
+    state_f = lambda x, v: (Vhigh - Vlow + 1) * (x - 1) + v
     bad_states = set()
     good_state = set()
 
     def get_formula_states(xcar_stop):
-        bst_cw = set() # Bad states at crosswalk
-        bst_prior = set() # Bad states prior to crosswalk
+        bst_cw = set()  # Bad states at crosswalk
+        bst_prior = set()  # Bad states prior to crosswalk
         # Having non-zero speed at crosswalk is a bad state:
-        for vi in range(1,Vhigh+1):
+        for vi in range(1, Vhigh + 1):
             state = state_f(xcar_stop, vi)
-            bst_cw |= {"S"+str(state)}
+            bst_cw |= {"S" + str(state)}
         # Having zero speed at any cell before crosswalk is a bad state:
-        for xi in range(1,xcar_stop):
+        for xi in range(1, xcar_stop):
             state = state_f(xi, 0)
-            bst_prior |= {"S"+str(state)}
-        gst = {"S" + str(state_f(xcar_stop,0))}
-        bad_cw = "" # Expression for bad states for crosswalk
-        bad_prior = "" # Expression for bad states before crosswalk
-        good = "" # Expression for good states
+            bst_prior |= {"S" + str(state)}
+        gst = {"S" + str(state_f(xcar_stop, 0))}
+        bad_cw = ""  # Expression for bad states for crosswalk
+        bad_prior = ""  # Expression for bad states before crosswalk
+        good = ""  # Expression for good states
         for st in list(gst):
             if good == "":
-                good = good + "\"" + st+"\""
+                good = good + '"' + st + '"'
             else:
-                good = good + "|\""+st+"\""
+                good = good + '|"' + st + '"'
         for st in list(bst_cw):
             if bad_cw == "":
-                bad_cw = bad_cw + "\"" + st+"\""
+                bad_cw = bad_cw + '"' + st + '"'
             else:
-                bad_cw = bad_cw + "|\""+st+"\""
+                bad_cw = bad_cw + '|"' + st + '"'
         for st in list(bst_prior):
             if bad_prior == "":
-                bad_prior = bad_prior + "\"" + st+"\""
+                bad_prior = bad_prior + '"' + st + '"'
             else:
-                bad_prior = bad_prior + "|\""+st+"\""
+                bad_prior = bad_prior + '|"' + st + '"'
         return good, bad_cw, bad_prior, gst, bst_cw, bst_prior
+
     good, bad_cw, bad_prior, gst, bst_cw, bst_prior = get_formula_states(xcar_stop)
 
     good_state |= gst
     bad_states |= bst_cw
     bad_states |= bst_prior
-    phi = "G("+good+" | !("+bad_cw+"|" + bad_prior+"))" # eventually good
+    phi = "G(" + good + " | !(" + bad_cw + "|" + bad_prior + "))"  # eventually good
     if true_env_type == "ped":
-        formula = "P=?[" + phi +"]" # Don't reach bad states and eventually reach good states
+        formula = "P=?[" + phi + "]"  # Don't reach bad states and eventually reach good states
     else:
-        formula = "P=?[G !(" + good +")]"
+        formula = "P=?[G !(" + good + ")]"
     print(formula)
     return xped, bad_states, good_state, formula
+
+
 # Construct backup controller:
 def construct_backup_controller(Ncar, Vlow, Vhigh):
     K_backup = dict()
-    for xcar in range(1,Ncar+1):
-        for vcar in range(Vlow, Vhigh+1):
+    for xcar in range(1, Ncar + 1):
+        for vcar in range(Vlow, Vhigh + 1):
             st = (xcar, vcar)
             end_st = []
             if xcar == Ncar:
                 end_st.append((xcar, vcar))
             elif vcar == 0:
-                xcar_p = min(Ncar, xcar+1)
+                xcar_p = min(Ncar, xcar + 1)
                 end_st.append((xcar, vcar))
-                end_st.append((xcar_p, vcar+1))
-                end_st.append((xcar, vcar+1))
+                end_st.append((xcar_p, vcar + 1))
+                end_st.append((xcar, vcar + 1))
             else:
-                xcar_p = min(Ncar, xcar+vcar)
+                xcar_p = min(Ncar, xcar + vcar)
                 end_st.append((xcar_p, vcar))
-                end_st.append((xcar_p, vcar-1))
+                end_st.append((xcar_p, vcar - 1))
                 if vcar < Vhigh:
-                    end_st.append((xcar_p, vcar+1))
+                    end_st.append((xcar_p, vcar + 1))
             K_backup[st] = end_st
     return K_backup
 
+
 # Creating the states of the markov chain for the system:
 # Returns product states S and (pos,vel) to state dictionary
 def system_states(Ncar, Vlow, Vhigh):
-    nS = Ncar*(Vhigh-Vlow+1)
-    state = lambda x,v: (Vhigh-Vlow+1)*(x-1) + v
+    nS = Ncar * (Vhigh - Vlow + 1)
+    state = lambda x, v: (Vhigh - Vlow + 1) * (x - 1) + v
     state_to_S = dict()
     S = set()
-    for xcar in range(1,Ncar+1):
-        for vcar in range(Vlow, Vhigh+1):
-            st = "S"+str(state(xcar, vcar))
-            state_to_S[xcar,vcar] = st
-            S|={st}
+    for xcar in range(1, Ncar + 1):
+        for vcar in range(Vlow, Vhigh + 1):
+            st = "S" + str(state(xcar, vcar))
+            state_to_S[xcar, vcar] = st
+            S |= {st}
     K_backup = construct_backup_controller(Ncar, Vlow, Vhigh)
     return S, state_to_S, K_backup
 
+
 def get_states_and_controllers(C, M, start_state, good_state, bad_states, K, K_backup, true_env_type="ped"):
-    true_env = str(1) #Sidewalk 3
+    true_env = str(1)  # Sidewalk 3
     true_env_type = true_env_type
     state_info = dict()
     state_info["start"] = start_state
@@ -218,31 +230,32 @@ def get_states_and_controllers(C, M, start_state, good_state, bad_states, K, K_b
     state_info["good"] = good_state
     if true_env_type == "ped":
         for st in list(good_state):
-            formula2 = 'P=?[F(\"'+st+'\")]'
+            formula2 = 'P=?[F("' + st + '")]'
     else:
         for st in list(good_state):
-            formula2 = 'P=?[G !(\"'+st+'\")]'
+            formula2 = 'P=?[G !("' + st + '")]'
     MC = call_MC(K, K_backup, C, true_env, true_env_type, state_info, M)
     result2 = MC.prob_TL(formula2)
     # MC = call_MC(K, K_backup, C, true_env, true_env_type, state_info, M)
     return MC, result2[start_state]
 
+
 # Construct state (x,v) back from state:
-def state_f(x,v):
-    S_value = (Vhigh-Vlow+1)*(x-1) + v
+def state_f(x, v):
+    S_value = (Vhigh - Vlow + 1) * (x - 1) + v
     return S_value
 
 
 # Generate probability points from Confusion Matrix:
 def gen_points(Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula, vmax):
-    points_ped = [] # Satisfaction probabilities for true env as ped
+    points_ped = []  # Satisfaction probabilities for true env as ped
 
-    vcar = 1 # Initial speed at starting point
-    start_state = "S"+str(state_f(1,vcar))
+    vcar = 1  # Initial speed at starting point
+    start_state = "S" + str(state_f(1, vcar))
     S, state_to_S, K_backup = system_states(Ncar, Vlow, Vhigh)
     O = {"ped", "obj", "empty"}
     tp_range = np.linspace(0.6, 0.999, num=100)
-    Nrand = 15 # Fifteen random instantiations of confusion matrix for each true positive rate
+    Nrand = 15  # Fifteen random instantiations of confusion matrix for each true positive rate
     tp_range_vals = []
     for tp_ped in tp_range:
         for i in range(Nrand):
@@ -251,22 +264,25 @@ def gen_points(Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula, vmax):
             K = K_des.construct_controllers(Ncar, Vlow, Vhigh, xped, vcar)
             Mped = MarkovChain(S, O, state_to_S)
             Cped = construct_CM(tp_ped, tp_obj, tp_emp)
-            MC, prob = get_states_and_controllers(Cped, Mped, start_state, good_state, bad_states, K, K_backup, true_env_type="ped")
+            MC, prob = get_states_and_controllers(
+                Cped, Mped, start_state, good_state, bad_states, K, K_backup, true_env_type="ped"
+            )
             # prob = MC.prob_TL(formula)
             points_ped.append(prob)
             tp_range_vals.append(tp_ped)
     print("Found probabilities for pedestrian env!")
     return points_ped, tp_range_vals
 
+
 # Generate probability points from Confusion Matrix:
 def gen_points_obj(Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula, vmax):
-    points_obj = [] # Satisfaction probabilities for true env as ped
-    vcar = 1 # Initial speed at starting point
-    start_state = "S"+str(state_f(1,vcar))
+    points_obj = []  # Satisfaction probabilities for true env as ped
+    vcar = 1  # Initial speed at starting point
+    start_state = "S" + str(state_f(1, vcar))
     S, state_to_S, K_backup = system_states(Ncar, Vlow, Vhigh)
     O = {"ped", "obj", "empty"}
     tp_range = np.linspace(0.3, 0.999, num=100)
-    Nrand = 15 # Fifteen random instantiations of confusion matrix for each true positive rate
+    Nrand = 15  # Fifteen random instantiations of confusion matrix for each true positive rate
     tp_range_vals = []
     for tp_obj in tp_range:
         for i in range(Nrand):
@@ -275,22 +291,25 @@ def gen_points_obj(Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula, vma
             K = K_des.construct_controllers(Ncar, Vlow, Vhigh, xped, vcar)
             Mobj = MarkovChain(S, O, state_to_S)
             Cobj = construct_CM(tp_ped, tp_obj, tp_emp)
-            MC, prob = get_states_and_controllers(Cobj, Mobj, start_state, good_state, bad_states, K, K_backup, true_env_type="obj")
+            MC, prob = get_states_and_controllers(
+                Cobj, Mobj, start_state, good_state, bad_states, K, K_backup, true_env_type="obj"
+            )
             # prob = MC.prob_TL(formula)
             points_obj.append(prob)
             tp_range_vals.append(tp_obj)
     print("Found probabilities for object env!")
     return points_obj, tp_range_vals
 
+
 # Generate probability points from Confusion Matrix:
 def gen_points_emp(Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula, vmax):
-    points_emp = [] # Satisfaction probabilities for true env as ped
-    vcar = 1 # Initial speed at starting point
-    start_state = "S"+str(state_f(1,vcar))
+    points_emp = []  # Satisfaction probabilities for true env as ped
+    vcar = 1  # Initial speed at starting point
+    start_state = "S" + str(state_f(1, vcar))
     S, state_to_S, K_backup = system_states(Ncar, Vlow, Vhigh)
     O = {"ped", "obj", "empty"}
     tp_range = np.linspace(0.6, 0.999, num=100)
-    Nrand = 15 # Fifteen random instantiations of confusion matrix for each true positive rate
+    Nrand = 15  # Fifteen random instantiations of confusion matrix for each true positive rate
     tp_range_vals = []
     for tp_emp in tp_range:
         for i in range(Nrand):
@@ -299,72 +318,151 @@ def gen_points_emp(Ncar, Vlow, Vhigh, xped, bad_states, good_state, formula, vma
             K = K_des.construct_controllers(Ncar, Vlow, Vhigh, xped, vcar)
             Memp = MarkovChain(S, O, state_to_S)
             Cemp = construct_CM(tp_ped, tp_obj, tp_emp)
-            MC, prob = get_states_and_controllers(Cemp, Memp, start_state, good_state, bad_states, K, K_backup, true_env_type="empty")
+            MC, prob = get_states_and_controllers(
+                Cemp, Memp, start_state, good_state, bad_states, K, K_backup, true_env_type="empty"
+            )
             # prob = MC.prob_TL(formula)
             points_emp.append(prob)
             tp_range_vals.append(tp_emp)
     print("Found probabilities for empty env!")
     return points_emp, tp_range_vals
 
+
 # Plot probability points:
-def plot_probabilities(points, tpped_vals, true_env, vmax, rand =True):
+def plot_probabilities(points, tpped_vals, true_env, vmax, rand=True):
     fig, ax = plt.subplots()
     ax.set_title(true_env)
-    plt.rcParams['text.usetex'] = True
-    ax.set_ylabel(r'$\mathbb{P}$')
-    plt.plot(tpped_vals, points, 'b*')
+    plt.rcParams["text.usetex"] = True
+    ax.set_ylabel(r"$\mathbb{P}$")
+    plt.plot(tpped_vals, points, "b*")
     lb = min(tpped_vals)
     ub = max(tpped_vals)
     if rand:
-        plt.savefig("saved_plots/points_"+true_env+"_lb"+str(lb)+"_ub"+str(ub)+"_vmax"+str(vmax)+"_N"+str(Ncar)+"rand.png")
+        plt.savefig(
+            "saved_plots/points_"
+            + true_env
+            + "_lb"
+            + str(lb)
+            + "_ub"
+            + str(ub)
+            + "_vmax"
+            + str(vmax)
+            + "_N"
+            + str(Ncar)
+            + "rand.png"
+        )
     else:
-        plt.savefig("saved_plots/points_"+true_env+"_lb"+str(lb)+"_ub"+str(ub)+"_vmax"+str(vmax)+"_N"+str(Ncar)+".png")
+        plt.savefig(
+            "saved_plots/points_"
+            + true_env
+            + "_lb"
+            + str(lb)
+            + "_ub"
+            + str(ub)
+            + "_vmax"
+            + str(vmax)
+            + "_N"
+            + str(Ncar)
+            + ".png"
+        )
+
 
 # Plot probability points:
-def plot_probabilities_bounds(points, tpped_vals, true_env, vmax, Ncar, ubounds, lbounds, rand =True):
+def plot_probabilities_bounds(points, tpped_vals, true_env, vmax, Ncar, ubounds, lbounds, rand=True):
     fig, ax = plt.subplots()
     # ax.set_title(true_env,fontsize=35)
     # plt.rcParams['text.usetex'] = True
     # plt.ylabel(r'$\mathbb{P}_{'+str(true_env)+'}$',fontsize=35)
     # plt.xlabel(r'$TP_{'+str(true_env)+'}$',fontsize=35)
-    ax.tick_params(axis='both', which='major', labelsize=35)
-    plt.plot(tpped_vals, points, 'b*',label='sampled')
+    ax.tick_params(axis="both", which="major", labelsize=35)
+    plt.plot(tpped_vals, points, "b*", label="sampled")
     if ubounds != []:
         ub_m, ub_c = ubounds
-        y_ub = ub_m*np.array(tpped_vals) + ub_c
-        plt.plot(tpped_vals, y_ub, 'r')
+        y_ub = ub_m * np.array(tpped_vals) + ub_c
+        plt.plot(tpped_vals, y_ub, "r")
 
     lb_m, lb_c = lbounds
-    y_lb = lb_m*np.array(tpped_vals) + lb_c
-    plt.plot(tpped_vals, y_lb, 'k',label='lower bound',linewidth=5)
+    y_lb = lb_m * np.array(tpped_vals) + lb_c
+    plt.plot(tpped_vals, y_lb, "k", label="lower bound", linewidth=5)
 
     lb = min(tpped_vals)
     ub = max(tpped_vals)
-    plt.legend(prop={'size': 35})
+    plt.legend(prop={"size": 35})
     plt.show()
     if rand:
-        plt.savefig("saved_plots/bounded_points_"+true_env+"_lb"+str(lb)+"_ub"+str(ub)+"_vmax"+str(vmax)+"_N"+str(Ncar)+"rand.png")
+        plt.savefig(
+            "saved_plots/bounded_points_"
+            + true_env
+            + "_lb"
+            + str(lb)
+            + "_ub"
+            + str(ub)
+            + "_vmax"
+            + str(vmax)
+            + "_N"
+            + str(Ncar)
+            + "rand.png"
+        )
     else:
-        plt.savefig("saved_plots/bounded_points_"+true_env+"_lb"+str(lb)+"_ub"+str(ub)+"_vmax"+str(vmax)+"_N"+str(Ncar)+".png")
+        plt.savefig(
+            "saved_plots/bounded_points_"
+            + true_env
+            + "_lb"
+            + str(lb)
+            + "_ub"
+            + str(ub)
+            + "_vmax"
+            + str(vmax)
+            + "_N"
+            + str(Ncar)
+            + ".png"
+        )
+
 
 def save_result(points_ped, tpped_vals, true_env, vmax, Ncar, rand=True):
     lb = min(tpped_vals)
     ub = max(tpped_vals)
     if rand:
-        fn = "results/points_"+true_env+"_lb"+str(lb)+"_ub"+str(ub)+"_vmax"+str(vmax)+"_N"+str(Ncar)+"rand.pkl"
+        fn = (
+            "results/points_"
+            + true_env
+            + "_lb"
+            + str(lb)
+            + "_ub"
+            + str(ub)
+            + "_vmax"
+            + str(vmax)
+            + "_N"
+            + str(Ncar)
+            + "rand.pkl"
+        )
     else:
-        fn = "results/points_"+true_env+"_lb"+str(lb)+"_ub"+str(ub)+"_vmax"+str(vmax)+"_N"+str(Ncar)+".pkl"
+        fn = (
+            "results/points_"
+            + true_env
+            + "_lb"
+            + str(lb)
+            + "_ub"
+            + str(ub)
+            + "_vmax"
+            + str(vmax)
+            + "_N"
+            + str(Ncar)
+            + ".pkl"
+        )
     with open(fn, "wb") as f:
         pkl.dump([points_ped, tpped_vals], f)
     f.close()
     return fn
 
+
 def load_result(fn):
     with open(fn, "rb") as f:
         points_ped, tpped_vals = pkl.load(f)
     f.close()
     return points_ped, tpped_vals
 
+
 def find_min_max_probs(result_tuple):
     max_probs = dict()
     min_probs = dict()
@@ -382,6 +480,7 @@ def find_min_max_probs(result_tuple):
             min_probs[tpped] = prob
     return max_probs, min_probs
 
+
 def bound_probs_obj(max_probs, min_probs):
     # Picking the two largest max_probs:
     sorted_max = sorted(max_probs.items(), key=lambda kv: kv[1])
@@ -408,10 +507,10 @@ def bound_probs_obj(max_probs, min_probs):
             break
 
     # Finding line:
-    ub_m = (pmax1 - pmax2)/(tp_max1 - tp_max2)
-    ub_c = pmax2 - ub_m*tp_max2
-    lb_m = (pmin1 - pmin2)/(tp_min1 - tp_min2)
-    lb_c = pmin2 - lb_m*tp_min2
+    ub_m = (pmax1 - pmax2) / (tp_max1 - tp_max2)
+    ub_c = pmax2 - ub_m * tp_max2
+    lb_m = (pmin1 - pmin2) / (tp_min1 - tp_min2)
+    lb_c = pmin2 - lb_m * tp_min2
     # Picking the two smallest min_probs:
     try:
         assert ub_m + ub_c <= 1 + 1e-4
@@ -421,6 +520,7 @@ def bound_probs_obj(max_probs, min_probs):
     lb = [lb_m, lb_c]
     return ub, lb
 
+
 def bound_probs_emp(max_probs, min_probs):
     # Picking the two largest max_probs:
     sorted_max = sorted(max_probs.items(), key=lambda kv: kv[1])
@@ -441,10 +541,10 @@ def bound_probs_emp(max_probs, min_probs):
     tp_min1, pmin1 = list(sorted_min.items())[0]
     tp_min2, pmin2 = list(sorted_min.items())[-1]
     # Finding line:
-    ub_m = (pmax1 - pmax2)/(tp_max1 - tp_max2)
-    ub_c = pmax2 - ub_m*tp_max2
-    lb_m = (pmin1 - pmin2)/(tp_min1 - tp_min2)
-    lb_c = pmin2 - lb_m*tp_min2
+    ub_m = (pmax1 - pmax2) / (tp_max1 - tp_max2)
+    ub_c = pmax2 - ub_m * tp_max2
+    lb_m = (pmin1 - pmin2) / (tp_min1 - tp_min2)
+    lb_c = pmin2 - lb_m * tp_min2
     # Picking the two smallest min_probs:
 
     try:
@@ -455,6 +555,7 @@ def bound_probs_emp(max_probs, min_probs):
     lb = [lb_m, lb_c]
     return ub, lb
 
+
 # +
 def bound_probs_ped(max_probs, min_probs):
     # Picking the two largest max_probs:
@@ -493,10 +594,10 @@ def bound_probs_ped(max_probs, min_probs):
     tp_min2, pmin2 = list(sorted_min.items())[-1]
 
     # Finding line:
-    ub_m = (pmax1 - pmax2)/(tp_max1 - tp_max2)
-    ub_c = pmax2 - ub_m*tp_max2
-    lb_m = (pmin1 - pmin2)/(tp_min1 - tp_min2)
-    lb_c = pmin2 - lb_m*tp_min2
+    ub_m = (pmax1 - pmax2) / (tp_max1 - tp_max2)
+    ub_c = pmax2 - ub_m * tp_max2
+    lb_m = (pmin1 - pmin2) / (tp_min1 - tp_min2)
+    lb_c = pmin2 - lb_m * tp_min2
     # Picking the two smallest min_probs:
     try:
         assert ub_m + ub_c <= 1 + 1e-4
@@ -507,22 +608,23 @@ def bound_probs_ped(max_probs, min_probs):
     lb = [lb_m, lb_c]
     return ub, lb
 
+
 def bound_probs_lp(tpx, proby):
     l = min(tpx)
     u = max(tpx)
     m = cp.Variable()
     b = cp.Variable()
 
-    alpha = (u**2 - l**2)/2
-    beta = (u-l)
-    obj =  alpha*m + beta*b
+    alpha = (u**2 - l**2) / 2
+    beta = u - l
+    obj = alpha * m + beta * b
     n = len(tpx)
 
-    A = np.array(tpx).reshape(1,-1)
-    B = np.ones((n,1))
-    C = np.array(proby).reshape(1,-1)
+    A = np.array(tpx).reshape(1, -1)
+    B = np.ones((n, 1))
+    C = np.array(proby).reshape(1, -1)
 
-    constraints = [A*m + B*b <=C]
+    constraints = [A * m + B * b <= C]
     prob = cp.Problem(cp.Maximize(obj), constraints)
     prob.solve()
     # pdb.set_trace()
@@ -536,7 +638,7 @@ def bound_probs_lp(tpx, proby):
 
 def derive_prob_bounds_lp(fn, true_env):
     points, tp_vals = load_result(fn)
-    result_tuple = [[tp_vals[i],points[i]] for i in range(len(tp_vals))]
+    result_tuple = [[tp_vals[i], points[i]] for i in range(len(tp_vals))]
     if true_env == "ped":
         ub, lb = bound_probs_lp(tp_vals, points)
     elif true_env == "obj":
@@ -550,7 +652,7 @@ def derive_prob_bounds(fn, true_env):
     points_ped, tpped_vals = load_result(fn)
     lb_ped = min(tpped_vals)
     ub_ped = max(tpped_vals)
-    result_tuple = [[tpped_vals[i],points_ped[i]] for i in range(len(tpped_vals))]
+    result_tuple = [[tpped_vals[i], points_ped[i]] for i in range(len(tpped_vals))]
     # Finding the max and min probabilities for each true positive value:
     max_probs, min_probs = find_min_max_probs(result_tuple)
     if true_env == "ped":
@@ -562,7 +664,7 @@ def derive_prob_bounds(fn, true_env):
     return ub, lb, points_ped, tpped_vals
 
 
-if __name__=='__main__':
+if __name__ == "__main__":
     vmax = 5
     Ncar = 15
     Vlow = 0
@@ -583,18 +685,18 @@ def derive_prob_bounds(fn, true_env):
         fn = save_result(points, tp_vals, true_env, vmax, Ncar)
     # Derive bounds and plot probability bounds:
     else:
-        fn = "data/points_"+true_env+"_lb0.6_ub0.999_vmax2_N6rand.pkl"
+        fn = "data/points_" + true_env + "_lb0.6_ub0.999_vmax2_N6rand.pkl"
 
     recompute_lb = False
     if recompute_lb:
         ubounds, lbounds, points, tp_vals = derive_prob_bounds_lp(fn, true_env)
-        with open("lb"+true_env+".pkl", "wb") as f:
+        with open("lb" + true_env + ".pkl", "wb") as f:
             pkl.dump([lbounds, points, tp_vals], f)
         f.close()
-        plot_probabilities_bounds(points, tp_vals, true_env, vmax, Ncar, ubounds, lbounds, rand =True)
+        plot_probabilities_bounds(points, tp_vals, true_env, vmax, Ncar, ubounds, lbounds, rand=True)
     else:
-        with open("lb"+true_env+".pkl", "rb") as f:
+        with open("lb" + true_env + ".pkl", "rb") as f:
             lbounds, points, tp_vals = pkl.load(f)
         f.close()
         ubounds = []
-        plot_probabilities_bounds(points, tp_vals, true_env, vmax, Ncar, ubounds, lbounds, rand =True)
+        plot_probabilities_bounds(points, tp_vals, true_env, vmax, Ncar, ubounds, lbounds, rand=True)
diff --git a/case_studies/evaluating_perception/case_study_new.ipynb b/case_studies/evaluating_perception/case_study_new.ipynb
index 77262f5f..15f3454b 100644
--- a/case_studies/evaluating_perception/case_study_new.ipynb
+++ b/case_studies/evaluating_perception/case_study_new.ipynb
@@ -30,14 +30,12 @@
    ],
    "source": [
     "# Import libraries\n",
-    "from pacti.iocontract.utils import getVarlist\n",
-    "from PIL import Image\n",
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
-    "import pdb\n",
-    "from case_study import * \n",
-    "import random\n",
     "import pickle as pkl\n",
-    "import collections"
+    "\n",
+    "from case_study import *\n",
+    "from PIL import Image\n",
+    "\n",
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -56,15 +54,15 @@
    "outputs": [],
    "source": [
     "TILESIZE = 50\n",
-    "ORIENTATIONS = {'n': 270, 'e': 0, 's': 90,'w':180, 'ne':315, 'nw':225, 'se':45, 'sw':135}\n",
+    "ORIENTATIONS = {\"n\": 270, \"e\": 0, \"s\": 90, \"w\": 180, \"ne\": 315, \"nw\": 225, \"se\": 45, \"sw\": 135}\n",
     "START_CROSSWALK = -1\n",
     "END_CROSSWALK = 2\n",
     "CROSSWALK_V = 1\n",
     "CROSSWALK_LOCATIONS = dict()\n",
     "ped_loc = []\n",
-    "for i, num in enumerate(range(2*START_CROSSWALK,2*(END_CROSSWALK+1))):\n",
-    "    CROSSWALK_LOCATIONS.update({i: (num/2, CROSSWALK_V)})\n",
-    "    ped_loc.append(num/2)\n"
+    "for i, num in enumerate(range(2 * START_CROSSWALK, 2 * (END_CROSSWALK + 1))):\n",
+    "    CROSSWALK_LOCATIONS.update({i: (num / 2, CROSSWALK_V)})\n",
+    "    ped_loc.append(num / 2)"
    ]
   },
   {
@@ -94,23 +92,23 @@
     }
    ],
    "source": [
-    "class GridCar():\n",
+    "class GridCar:\n",
     "    def __init__(self):\n",
     "        self.xc_init = 0\n",
     "        self.vc_init = 0\n",
     "        self.ped = 1\n",
     "        self.N = 10\n",
     "        self.Vmax = 2\n",
-    "        self.xped = 10-CROSSWALK_V\n",
+    "        self.xped = 10 - CROSSWALK_V\n",
     "        self.main_dir = os.path.dirname(os.path.dirname(os.path.realpath(\"__file__\")))\n",
-    "        self.car_fig = self.main_dir + '/evaluating_perception/imglib/red_car.png'\n",
-    "        self.ped_fig = self.main_dir + '/evaluating_perception/imglib/pedestrian_img.png'\n",
+    "        self.car_fig = self.main_dir + \"/evaluating_perception/imglib/red_car.png\"\n",
+    "        self.ped_fig = self.main_dir + \"/evaluating_perception/imglib/pedestrian_img.png\"\n",
     "        self.car_data = [9, 0]\n",
     "        if self.ped == 1:\n",
     "            self.ped_data = [CROSSWALK_V, min(ped_loc)]\n",
     "        else:\n",
     "            self.ped_data = None\n",
-    "    \n",
+    "\n",
     "    def dynamics(self):\n",
     "        transitions = dict()\n",
     "        for x in range(0, self.N):\n",
@@ -119,7 +117,7 @@
     "                x_succ.append(x + v)\n",
     "            transitions[x] = x_succ\n",
     "        return transitions\n",
-    "    \n",
+    "\n",
     "    def plot_grid(self):\n",
     "        size = [1, self.N]\n",
     "        x_min = 0\n",
@@ -127,9 +125,9 @@
     "        y_min = 0\n",
     "        y_max = size[1] * TILESIZE\n",
     "        # x_min, x_max, y_min, y_max = get_map_corners(map)\n",
-    "        \n",
+    "\n",
     "        fig, ax = plt.subplots()\n",
-    "        ax.axis('equal')\n",
+    "        ax.axis(\"equal\")\n",
     "        ax.set_xlim(x_min, x_max)\n",
     "        ax.set_ylim(y_min, y_max)\n",
     "        ax.xaxis.set_visible(False)\n",
@@ -137,12 +135,14 @@
     "\n",
     "        # fill in the road regions\n",
     "        road_tiles = []\n",
-    "        width_tiles = np.arange(0,size[0]+1)*2*TILESIZE\n",
-    "        lanes_tiles = np.arange(0,size[1]+1)*TILESIZE\n",
-    "        \n",
-    "        for i in np.arange(0,size[0]):\n",
-    "            for k in np.arange(0,size[1]+1):\n",
-    "                tile = patches.Rectangle((width_tiles[i],lanes_tiles[k]),2*TILESIZE,TILESIZE,linewidth=1,facecolor='k', alpha=0.4)\n",
+    "        width_tiles = np.arange(0, size[0] + 1) * 2 * TILESIZE\n",
+    "        lanes_tiles = np.arange(0, size[1] + 1) * TILESIZE\n",
+    "\n",
+    "        for i in np.arange(0, size[0]):\n",
+    "            for k in np.arange(0, size[1] + 1):\n",
+    "                tile = patches.Rectangle(\n",
+    "                    (width_tiles[i], lanes_tiles[k]), 2 * TILESIZE, TILESIZE, linewidth=1, facecolor=\"k\", alpha=0.4\n",
+    "                )\n",
     "                road_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(road_tiles, match_original=True))\n",
     "\n",
@@ -150,76 +150,79 @@
     "        crosswalk_tiles = []\n",
     "        for item in CROSSWALK_LOCATIONS.keys():\n",
     "            if item % 2 == 0:\n",
-    "                color = 'silver'\n",
+    "                color = \"silver\"\n",
     "                alpha = 0.5\n",
     "            else:\n",
-    "                color = 'k'\n",
+    "                color = \"k\"\n",
     "                alpha = 0.5\n",
-    "            width = CROSSWALK_LOCATIONS[item][0]*TILESIZE\n",
-    "            lanes = CROSSWALK_LOCATIONS[item][1]*TILESIZE\n",
-    "            tile = patches.Rectangle((width,lanes),TILESIZE/2,TILESIZE,linewidth=1,facecolor=color, alpha=alpha)\n",
+    "            width = CROSSWALK_LOCATIONS[item][0] * TILESIZE\n",
+    "            lanes = CROSSWALK_LOCATIONS[item][1] * TILESIZE\n",
+    "            tile = patches.Rectangle((width, lanes), TILESIZE / 2, TILESIZE, linewidth=1, facecolor=color, alpha=alpha)\n",
     "            crosswalk_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(crosswalk_tiles, match_original=True))\n",
     "        # TODO: Add cross-walk tile names next to the cells\n",
     "        plt.gca().invert_yaxis()\n",
     "        return ax\n",
-    "    \n",
-    "    \n",
+    "\n",
     "    def plot_car(self, ax):\n",
     "        y_tile, speed = self.car_data\n",
-    "        theta_d = 270 # Rotate car to face north\n",
-    "        x = (1) * TILESIZE/2\n",
+    "        theta_d = 270  # Rotate car to face north\n",
+    "        x = (1) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        car_fig = Image.open(self.car_fig)\n",
     "        car_fig = car_fig.rotate(theta_d, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(car_fig, zorder=1, interpolation='bilinear', extent=[x+2, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            car_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 2, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    def plot_ped(self, ax):\n",
     "        y_tile, x_tile = self.ped_data\n",
-    "        x = (x_tile) * TILESIZE/2\n",
+    "        x = (x_tile) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        ped_fig = Image.open(self.ped_fig)\n",
     "        ped_fig = ped_fig.rotate(180, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(ped_fig, zorder=1, interpolation='bilinear', extent=[x+4, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            ped_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 4, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    # Function to calculate all system states:\n",
     "    def construct_transitions(self):\n",
     "        T = dict()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
     "                st = (xcar, vcar)\n",
     "                end_st = []\n",
     "                if xcar == self.N:\n",
     "                    end_st.append((xcar, vcar))\n",
     "                elif vcar == 0:\n",
-    "                    xcar_p = min(self.N, xcar+1)\n",
+    "                    xcar_p = min(self.N, xcar + 1)\n",
     "                    end_st.append((xcar, vcar))\n",
-    "                    end_st.append((xcar_p, vcar+1))\n",
-    "                    end_st.append((xcar, vcar+1))\n",
+    "                    end_st.append((xcar_p, vcar + 1))\n",
+    "                    end_st.append((xcar, vcar + 1))\n",
     "                else:\n",
-    "                    xcar_p = min(self.N, xcar+vcar)\n",
+    "                    xcar_p = min(self.N, xcar + vcar)\n",
     "                    end_st.append((xcar_p, vcar))\n",
-    "                    end_st.append((xcar_p, vcar-1))\n",
+    "                    end_st.append((xcar_p, vcar - 1))\n",
     "                    if vcar < Vhigh:\n",
-    "                        end_st.append((xcar_p, vcar+1))\n",
+    "                        end_st.append((xcar_p, vcar + 1))\n",
     "                T[st] = end_st\n",
     "        return T\n",
-    "    \n",
+    "\n",
     "    # System states for pedestrian\n",
     "    def system_states_example_ped(self):\n",
-    "        nS = self.N*(self.Vmax+1)\n",
-    "        state = lambda x,v: (self.Vmax+1)*(x-1) + v\n",
+    "        nS = self.N * (self.Vmax + 1)\n",
+    "        state = lambda x, v: (self.Vmax + 1) * (x - 1) + v\n",
     "        state_to_S = dict()\n",
     "        S = set()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
-    "                st = \"S\"+str(state(xcar, vcar))\n",
-    "                state_to_S[xcar,vcar] = st\n",
-    "                S|={st}\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
+    "                st = \"S\" + str(state(xcar, vcar))\n",
+    "                state_to_S[xcar, vcar] = st\n",
+    "                S |= {st}\n",
     "        T = self.construct_transitions()\n",
     "        return S, state_to_S, T\n",
     "\n",
@@ -267,7 +270,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "# ubounds_ped, lbounds_ped, points_ped, tpped_vals = derive_prob_bounds_lp(fn, true_env)\n",
     "# plot_probabilities_bounds(points_ped, tpped_vals, true_env, vmax, Ncar, ubounds_ped, lbounds_ped, rand =True)\n",
@@ -283,7 +286,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "# ubounds_obj, lbounds_obj, points_obj, tpobj_vals = derive_prob_bounds_lp(fn, true_env)\n",
     "# plot_probabilities_bounds(points_obj, tpobj_vals, true_env, vmax, Ncar, ubounds_obj, lbounds_obj, rand =True)\n",
@@ -299,7 +302,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "# ubounds_emp, lbounds_emp, points_emp, tpemp_vals = derive_prob_bounds_lp(fn, true_env)\n",
     "# plot_probabilities_bounds(points_emp, tpemp_vals, true_env, vmax, Ncar, ubounds_emp, lbounds_emp, rand =True)\n",
@@ -361,78 +364,67 @@
     "## Control contract for pedestrian class\n",
     "def control_contract_ped(lbounds_ped):\n",
     "    lb_m, lb_c = lbounds_ped\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_ped\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"tp_ped\": -1}, \"constant\": -0.6}],\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"tp_ped\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"tp_ped\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_obj(lbounds_obj):\n",
     "    lb_m, lb_c = lbounds_obj\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            #             {\"coefficients\":{\"tp_obj\":1},\n",
+    "            #             \"constant\":0.999},\n",
+    "            {\"coefficients\": {\"tp_obj\": -1}, \"constant\": -0.3}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "#             {\"coefficients\":{\"tp_obj\":1},\n",
-    "#             \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_obj\":-1},\n",
-    "            \"constant\":-0.3}\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"tp_obj\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_obj\":-1, \"tp_obj\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_emp(lbounds_emp):\n",
     "    lb_m, lb_c = lbounds_emp\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            #             {\"coefficients\":{\"tp_emp\":1},\n",
+    "            #             \"constant\":0.999},\n",
+    "            {\"coefficients\": {\"tp_emp\": -1}, \"constant\": -0.6}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "#             {\"coefficients\":{\"tp_emp\":1},\n",
-    "#             \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_emp\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"tp_emp\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_emp\":-1, \"tp_emp\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cped = PolyhedralContract.from_dict(control_contract_ped(lbounds_ped))\n",
     "Cobj = PolyhedralContract.from_dict(control_contract_obj(lbounds_obj))\n",
     "Cemp = PolyhedralContract.from_dict(control_contract_emp(lbounds_emp))\n",
     "\n",
     "print(\"Controller Contract for pedestrian class:\\n\" + str(Cped) + \"\\n\")\n",
-    "print(\"Controller Contract for object class:\\n\" + str(Cobj)+ \"\\n\")\n",
-    "print(\"Controller Contract for empty class:\\n\" + str(Cemp)+ \"\\n\")\n"
+    "print(\"Controller Contract for object class:\\n\" + str(Cobj) + \"\\n\")\n",
+    "print(\"Controller Contract for empty class:\\n\" + str(Cemp) + \"\\n\")"
    ]
   },
   {
@@ -456,7 +448,7 @@
    ],
    "source": [
     "# Construct merger of contracts:\n",
-    "C_controller = Cped.merge(Cobj) \n",
+    "C_controller = Cped.merge(Cobj)\n",
     "C_controller = C_controller.merge(Cemp)\n",
     "\n",
     "print(\"Merged Controller Contract for all object classes:\\n\" + str(C_controller) + \"\\n\")"
@@ -485,34 +477,24 @@
     "## System contract: P >= 0.99\n",
     "def system_contract_dist():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"d\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "        \"input_vars\": [\"d\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"d\": -1}, \"constant\": 0}, {\"coefficients\": {\"d\": 1}, \"constant\": 10}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"d\": -0.099}, \"constant\": -0.99},\n",
+    "            #             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"d\": -0.08}, \"constant\": -0.8},\n",
+    "            #             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"d\": -0.095}, \"constant\": -0.95},\n",
+    "            #             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
+    "            #             \"constant\":1}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"d\":-1}, \"constant\":0},\n",
-    "            {\"coefficients\":{\"d\":1}, \"constant\":10}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1, \"d\":-0.099},\n",
-    "            \"constant\":-0.99},\n",
-    "#             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1, \"d\":-0.08},\n",
-    "            \"constant\":-0.8},\n",
-    "#             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1, \"d\":-0.095},\n",
-    "            \"constant\":-0.95},\n",
-    "#             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
-    "#             \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys_dist = PolyhedralContract.from_dict(system_contract_dist())\n",
     "print(\"System Contract (distance bounds):\\n\" + str(Csys_dist) + \"\\n\")"
    ]
@@ -539,7 +521,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_dist = Csys_dist.quotient(C_controller)\n",
-    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist)+\"\\n\")"
+    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist) + \"\\n\")"
    ]
   }
  ],
diff --git a/case_studies/evaluating_perception/design_controller4.py b/case_studies/evaluating_perception/design_controller4.py
index 0d1b93bf..e339b502 100644
--- a/case_studies/evaluating_perception/design_controller4.py
+++ b/case_studies/evaluating_perception/design_controller4.py
@@ -2,14 +2,9 @@
 
 import logging
 
-import numpy as np
-from tulip import spec, synth, hybrid, transys
-from polytope import box2poly
-from tulip.abstract import prop2part, discretize
-from tulip.abstract.plot import plot_partition
+from tulip import spec, synth
 from tulip.dumpsmach import write_python_case
-from tulip.spec.gr1_fragment import response_to_gr1
-import pdb
+
 # This script invokes TuLiP to construct a controller for the system with respect to a
 # temporal logic specification based on the observed outputs of the perception algorithm
 # Inputs to the controller synthesis function are: discrete_dynamics (disc_dynamics),
@@ -17,202 +12,292 @@
 # cell/set of initial env. variables (env_init), cell/set of initial system variables (sys_init),
 # enviornment and system safety and progress specifications: env_safe/env_prog and sys_safe/sys_prog.
 
+
 def design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog):
     logging.basicConfig(level=logging.WARNING)
     show = False
 
     # Constructing GR1spec from environment and systems specifications:
-    specs = spec.GRSpec(env_vars, sys_vars, env_init, sys_init,
-                        env_safe, sys_safe, env_prog, sys_prog)
+    specs = spec.GRSpec(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     specs.moore = True
-    specs.qinit = '\E \A'
+    specs.qinit = "\E \A"
 
     # Synthesize
     ctrl = synth.synthesize(specs)
-    assert ctrl is not None, 'unrealizable'
+    assert ctrl is not None, "unrealizable"
 
     return ctrl
 
+
 # Function constructing transition system, specification variables for pedestrian/car example:
 # Takes as input the geometry of the sidewalk:
 # Ncar: No. of cells of the car, Nped: No. of cells of the crosswalk, xped: initial cell number of pedestrian, xcar: initial cell of car, vcar: initial velocity of car, xcross_start: Cell number of road at which the crosswalk starts
 # Vlow: Lower integer speed bound for car, Vhigh: Upper integer speed bound for car
 def not_pedestrianK(Ncar, xcar, vcar, Vlow, Vhigh, xped):
     sys_vars = {}
-    sys_vars['xcar'] = (1, Ncar)
-    sys_vars['vcar'] = (Vlow, Vhigh)
+    sys_vars["xcar"] = (1, Ncar)
+    sys_vars["vcar"] = (Vlow, Vhigh)
     env_vars = {}
-    env_vars['xobj'] = (0,1) # Difficult to have a set of just 1
+    env_vars["xobj"] = (0, 1)  # Difficult to have a set of just 1
 
-    sys_init = {'xcar='+str(xcar), 'vcar='+str(vcar)}
-    env_init = {'xobj='+str(1)}
+    sys_init = {"xcar=" + str(xcar), "vcar=" + str(vcar)}
+    env_init = {"xobj=" + str(1)}
 
-     # Test lines:
-    sys_init = {'xcar='+str(xcar), 'vcar='+str(vcar)}
-    env_init = {'xobj='+str(1)}
+    # Test lines:
+    sys_init = {"xcar=" + str(xcar), "vcar=" + str(vcar)}
+    env_init = {"xobj=" + str(1)}
 
-    sys_prog = set() # For now, no need to have progress
+    sys_prog = set()  # For now, no need to have progress
     env_prog = set()
 
     sys_safe = set()
-    env_safe = {'xobj=1 -> X(xobj=1)'}
+    env_safe = {"xobj=1 -> X(xobj=1)"}
 
     # Object controllers: If you see an object that is not a pedestrian, then keep moving:
     # spec_k = {'(xobj=1)->X(vcar=1)'}
     # sys_safe |= spec_k
 
     for vi in range(Vhigh, 1, -1):
-        spec_k = {'(xobj=1 && vcar='+str(vi)+')->X(vcar='+str(vi-1)+')'}
+        spec_k = {"(xobj=1 && vcar=" + str(vi) + ")->X(vcar=" + str(vi - 1) + ")"}
         sys_safe |= spec_k
-    spec_k = {'(xobj=1 && vcar=1) -> X(vcar=1)'}
+    spec_k = {"(xobj=1 && vcar=1) -> X(vcar=1)"}
     sys_safe |= spec_k
     # Add system dynamics to safety specs:
-    for ii in range(1, Ncar+1):
-        for vi in range(Vlow, Vhigh+1):
-            if vi==0:
-                spec_ii = {'((xcar='+str(ii)+') && (vcar=0))-> X((vcar=1) && xcar='+str(ii)+')'}
-                sys_safe|=spec_ii
+    for ii in range(1, Ncar + 1):
+        for vi in range(Vlow, Vhigh + 1):
+            if vi == 0:
+                spec_ii = {"((xcar=" + str(ii) + ") && (vcar=0))-> X((vcar=1) && xcar=" + str(ii) + ")"}
+                sys_safe |= spec_ii
             elif vi == Vhigh:
-                xf_ii = min(ii+vi, Ncar)
-                spec_ii = {'((xcar='+str(ii)+') && (vcar='+str(vi)+'))-> X((vcar='+str(vi)+'|| vcar='+str(vi-1)+') && xcar='+str(xf_ii)+')'}
-                sys_safe|=spec_ii
+                xf_ii = min(ii + vi, Ncar)
+                spec_ii = {
+                    "((xcar="
+                    + str(ii)
+                    + ") && (vcar="
+                    + str(vi)
+                    + "))-> X((vcar="
+                    + str(vi)
+                    + "|| vcar="
+                    + str(vi - 1)
+                    + ") && xcar="
+                    + str(xf_ii)
+                    + ")"
+                }
+                sys_safe |= spec_ii
             else:
-                xf_ii = min(ii+vi, Ncar)
-                spec_ii = {'((xcar='+str(ii)+') && (vcar='+str(vi)+'))-> X((vcar='+str(vi)+'|| vcar='+str(vi-1)+'|| vcar='+str(vi+1)+') && xcar='+str(xf_ii)+')'}
-                sys_safe|=spec_ii
+                xf_ii = min(ii + vi, Ncar)
+                spec_ii = {
+                    "((xcar="
+                    + str(ii)
+                    + ") && (vcar="
+                    + str(vi)
+                    + "))-> X((vcar="
+                    + str(vi)
+                    + "|| vcar="
+                    + str(vi - 1)
+                    + "|| vcar="
+                    + str(vi + 1)
+                    + ") && xcar="
+                    + str(xf_ii)
+                    + ")"
+                }
+                sys_safe |= spec_ii
     return env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog
 
+
 # Controller for not_pedestrian observation:
 def pedestrianK(Ncar, xcar, vcar, Vlow, Vhigh, xped):
     sys_vars = {}
-    sys_vars['xcar'] = (1, Ncar)
-    sys_vars['vcar'] = (Vlow, Vhigh)
+    sys_vars["xcar"] = (1, Ncar)
+    sys_vars["vcar"] = (Vlow, Vhigh)
     env_vars = {}
-    env_vars['xped'] = (0,1) # Pedestrian is present or absent
+    env_vars["xped"] = (0, 1)  # Pedestrian is present or absent
 
-    sys_init = {'xcar='+str(xcar), 'vcar='+str(vcar)}
-    env_init = {'xped='+str(xped)}
+    sys_init = {"xcar=" + str(xcar), "vcar=" + str(vcar)}
+    env_init = {"xped=" + str(xped)}
 
     # Test lines:
     # sys_init = {'xcar='+str(xcar)}
     env_init = set()
     # ========================= #
-    sys_prog = set() # For now, no need to have progress
+    sys_prog = set()  # For now, no need to have progress
     env_prog = set()
-    xcar_jj = (xped-1) # eventual goal location for car
-    #sys_prog = set({'xcar = '+str(xcar_jj)})
+    xcar_jj = xped - 1  # eventual goal location for car
+    # sys_prog = set({'xcar = '+str(xcar_jj)})
 
     sys_safe = set()
     env_safe = set()
 
     # Environment safety specs: Static pedestrian
     for xi in range(0, 2):
-        env_safe |= {'xped='+str(xi)+'-> X(xped='+str(xi)+')'}
+        env_safe |= {"xped=" + str(xi) + "-> X(xped=" + str(xi) + ")"}
 
     # Safety specifications to specify that car must stop before pedestrian:
-    sys_safe |= {'((xped = 1) ||!(xcar = '+str(xcar_jj)+' && vcar = 0))'}
+    sys_safe |= {"((xped = 1) ||!(xcar = " + str(xcar_jj) + " && vcar = 0))"}
     car_states = ""
-    for xcar_ii in range(xcar_jj, Ncar+1):
+    for xcar_ii in range(xcar_jj, Ncar + 1):
         if car_states == "":
-            car_states = "xcar = "+str(xcar_ii)
+            car_states = "xcar = " + str(xcar_ii)
         else:
             car_states = car_states + " || xcar = " + str(xcar_ii)
-    sys_safe |= {'(!(xped = 1)||!('+car_states+')||(vcar = 0 && xcar = '+str(xcar_jj)+'))'}
+    sys_safe |= {"(!(xped = 1)||!(" + car_states + ")||(vcar = 0 && xcar = " + str(xcar_jj) + "))"}
 
     # Safety specs for car to not stop before car reaching pedestrian sidewalk
     for xi in range(1, xcar_jj):
-        sys_safe |= {'!(xcar = '+str(xi)+' && vcar = 0)'}
+        sys_safe |= {"!(xcar = " + str(xi) + " && vcar = 0)"}
 
     # Add system dynamics to safety specs:
-    for ii in range(1, Ncar+1):
-        for vi in range(Vlow, Vhigh+1):
-            if vi==0:
-                spec_ii = {'((xcar='+str(ii)+') && (vcar=0))-> X((vcar=0 || vcar=1) && xcar='+str(ii)+')'}
-                sys_safe|=spec_ii
+    for ii in range(1, Ncar + 1):
+        for vi in range(Vlow, Vhigh + 1):
+            if vi == 0:
+                spec_ii = {"((xcar=" + str(ii) + ") && (vcar=0))-> X((vcar=0 || vcar=1) && xcar=" + str(ii) + ")"}
+                sys_safe |= spec_ii
             elif vi == Vhigh:
-                xf_ii = min(ii+vi, Ncar)
-                spec_ii = {'((xcar='+str(ii)+') && (vcar='+str(vi)+'))-> X((vcar='+str(vi)+'|| vcar='+str(vi-1)+') && xcar='+str(xf_ii)+')'}
-                sys_safe|=spec_ii
+                xf_ii = min(ii + vi, Ncar)
+                spec_ii = {
+                    "((xcar="
+                    + str(ii)
+                    + ") && (vcar="
+                    + str(vi)
+                    + "))-> X((vcar="
+                    + str(vi)
+                    + "|| vcar="
+                    + str(vi - 1)
+                    + ") && xcar="
+                    + str(xf_ii)
+                    + ")"
+                }
+                sys_safe |= spec_ii
             else:
-                xf_ii = min(ii+vi, Ncar)
-                spec_ii = {'((xcar='+str(ii)+') && (vcar='+str(vi)+'))-> X((vcar='+str(vi)+'|| vcar='+str(vi-1)+'|| vcar='+str(vi+1)+') && xcar='+str(xf_ii)+')'}
-                sys_safe|=spec_ii
+                xf_ii = min(ii + vi, Ncar)
+                spec_ii = {
+                    "((xcar="
+                    + str(ii)
+                    + ") && (vcar="
+                    + str(vi)
+                    + "))-> X((vcar="
+                    + str(vi)
+                    + "|| vcar="
+                    + str(vi - 1)
+                    + "|| vcar="
+                    + str(vi + 1)
+                    + ") && xcar="
+                    + str(xf_ii)
+                    + ")"
+                }
+                sys_safe |= spec_ii
     return env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog
 
+
 # Controller for empty observation:
 def emptyK(Ncar, xcar, vcar, Vlow, Vhigh, xped):
     sys_vars = {}
-    sys_vars['xcar'] = (1, Ncar)
-    sys_vars['vcar'] = (Vlow, Vhigh)
+    sys_vars["xcar"] = (1, Ncar)
+    sys_vars["vcar"] = (Vlow, Vhigh)
     env_vars = {}
-    env_vars['xempty'] = (0,1) # Pavement is empty
+    env_vars["xempty"] = (0, 1)  # Pavement is empty
 
-    sys_init = {'xcar='+str(xcar), 'vcar='+str(vcar)}
-    env_init = {'xempty='+str(1)}
-     # Test lines:
-    sys_init = {'xcar='+str(xcar), 'vcar='+str(vcar)}
-    env_init = {'xempty='+str(1)}
+    sys_init = {"xcar=" + str(xcar), "vcar=" + str(vcar)}
+    env_init = {"xempty=" + str(1)}
+    # Test lines:
+    sys_init = {"xcar=" + str(xcar), "vcar=" + str(vcar)}
+    env_init = {"xempty=" + str(1)}
 
-    sys_prog = set() # For now, no need to have progress
+    sys_prog = set()  # For now, no need to have progress
     env_prog = set()
 
     sys_safe = set()
 
     # Env safe spec: If env is empty, it always remains empty
-    env_spec = {'xempty=1 -> X(xempty=1)'}
+    env_spec = {"xempty=1 -> X(xempty=1)"}
     env_safe = set()
     env_safe |= env_spec
 
     # Environment safety specs: Static pedestrian
     # env_safe |= {'xped='+str(xped)+'-> X(xped='+str(xped)+')'}
     # Spec: If you don't see an object, keep moving:
-    spec_k = {'(xempty=1 && vcar=0)->X(vcar=1)'}
+    spec_k = {"(xempty=1 && vcar=0)->X(vcar=1)"}
     sys_safe |= spec_k
     for vi in range(1, Vhigh):
-        spec_k = {'(xempty=1 && vcar='+str(vi)+')->X(vcar='+str(vi+1)+')'}
+        spec_k = {"(xempty=1 && vcar=" + str(vi) + ")->X(vcar=" + str(vi + 1) + ")"}
         sys_safe |= spec_k
-    spec_k = {'(xempty=1 && vcar='+str(Vhigh)+')->X(vcar='+str(Vhigh)+')'}
+    spec_k = {"(xempty=1 && vcar=" + str(Vhigh) + ")->X(vcar=" + str(Vhigh) + ")"}
     sys_safe |= spec_k
 
     # Add system dynamics to safety specs:
-    for ii in range(1, Ncar+1):
-        for vi in range(Vlow, Vhigh+1):
-            if vi==0:
-                spec_ii = {'((xcar='+str(ii)+') && (vcar=0))-> X((vcar=1) && xcar='+str(ii)+')'}
-                sys_safe|=spec_ii
+    for ii in range(1, Ncar + 1):
+        for vi in range(Vlow, Vhigh + 1):
+            if vi == 0:
+                spec_ii = {"((xcar=" + str(ii) + ") && (vcar=0))-> X((vcar=1) && xcar=" + str(ii) + ")"}
+                sys_safe |= spec_ii
             elif vi == Vhigh:
-                xf_ii = min(ii+vi, Ncar)
-                spec_ii = {'((xcar='+str(ii)+') && (vcar='+str(vi)+'))-> X((vcar='+str(vi)+'|| vcar='+str(vi-1)+') && xcar='+str(xf_ii)+')'}
-                sys_safe|=spec_ii
+                xf_ii = min(ii + vi, Ncar)
+                spec_ii = {
+                    "((xcar="
+                    + str(ii)
+                    + ") && (vcar="
+                    + str(vi)
+                    + "))-> X((vcar="
+                    + str(vi)
+                    + "|| vcar="
+                    + str(vi - 1)
+                    + ") && xcar="
+                    + str(xf_ii)
+                    + ")"
+                }
+                sys_safe |= spec_ii
             else:
-                xf_ii = min(ii+vi, Ncar)
-                spec_ii = {'((xcar='+str(ii)+') && (vcar='+str(vi)+'))-> X((vcar='+str(vi)+'|| vcar='+str(vi-1)+'|| vcar='+str(vi+1)+') && xcar='+str(xf_ii)+')'}
-                sys_safe|=spec_ii
+                xf_ii = min(ii + vi, Ncar)
+                spec_ii = {
+                    "((xcar="
+                    + str(ii)
+                    + ") && (vcar="
+                    + str(vi)
+                    + "))-> X((vcar="
+                    + str(vi)
+                    + "|| vcar="
+                    + str(vi - 1)
+                    + "|| vcar="
+                    + str(vi + 1)
+                    + ") && xcar="
+                    + str(xf_ii)
+                    + ")"
+                }
+                sys_safe |= spec_ii
     return env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog
 
+
 def pretty_print_specs(spec_set, spec_name):
     print("=============================================")
     print(spec_name)
     for spec in spec_set:
         print(spec)
+
+
 # +
 # Design controller:
 def construct_controllers(Ncar, Vlow, Vhigh, xped, vcar):
     # Simple example of pedestrian crossing street:
     xcar = 1
     # When a pedestrian is observed:
-    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = pedestrianK(Ncar, xcar, vcar, Vlow, Vhigh, xped)
+    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = pedestrianK(
+        Ncar, xcar, vcar, Vlow, Vhigh, xped
+    )
     # pretty_print_specs(sys_safe, "sys safe")
     Kped = design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     write_python_case("ped_controller.py", Kped)
 
     # When something other than a pedestrian is observed:
-    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = not_pedestrianK(Ncar, xcar, vcar, Vlow, Vhigh, xped)
+    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = not_pedestrianK(
+        Ncar, xcar, vcar, Vlow, Vhigh, xped
+    )
     Knot_ped = design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     write_python_case("not_ped_controller.py", Knot_ped)
 
     # When nothing is observed:
-    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = emptyK(Ncar, xcar, vcar, Vlow, Vhigh, xped)
+    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = emptyK(
+        Ncar, xcar, vcar, Vlow, Vhigh, xped
+    )
     Kempty = design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     write_python_case("empty_controller.py", Kempty)
 
@@ -223,7 +308,8 @@ def construct_controllers(Ncar, Vlow, Vhigh, xped, vcar):
 
     return K
 
-if __name__=='__main__':
+
+if __name__ == "__main__":
     # Simple example of pedestrian crossing street:
     Ncar = 5
     Vhigh = 1
@@ -233,17 +319,23 @@ def construct_controllers(Ncar, Vlow, Vhigh, xped, vcar):
     xped = 3
     xcross_start = 3
     # When a pedestrian is observed:
-    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = pedestrianK(Ncar, xcar, vcar, Vlow, Vhigh, xped)
+    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = pedestrianK(
+        Ncar, xcar, vcar, Vlow, Vhigh, xped
+    )
     Kped = design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     write_python_case("ped_controller.py", Kped)
 
     # When something other than a pedestrian is observed:
-    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = not_pedestrianK(Ncar, xcar, vcar, Vlow, Vhigh, xped)
+    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = not_pedestrianK(
+        Ncar, xcar, vcar, Vlow, Vhigh, xped
+    )
     Knot_ped = design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     write_python_case("not_ped_controller.py", Knot_ped)
 
     # When nothing is observed:
-    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = emptyK(Ncar, xcar, vcar, Vlow, Vhigh, xped)
+    env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog = emptyK(
+        Ncar, xcar, vcar, Vlow, Vhigh, xped
+    )
     Kempty = design_C(env_vars, sys_vars, env_init, sys_init, env_safe, sys_safe, env_prog, sys_prog)
     write_python_case("empty_controller.py", Kempty)
 # -
diff --git a/case_studies/evaluating_perception/empty_controller.py b/case_studies/evaluating_perception/empty_controller.py
index ccbc39af..03dd3e71 100644
--- a/case_studies/evaluating_perception/empty_controller.py
+++ b/case_studies/evaluating_perception/empty_controller.py
@@ -11,9 +11,10 @@ class TulipStrategy(object):
     Automatically generated by tulip.dumpsmach on 2023-01-22 06:48:42 UTC
     To learn more about TuLiP, visit http://tulip-control.org
     """
+
     def __init__(self):
         self.state = 4
-        self.input_vars = ['xempty']
+        self.input_vars = ["xempty"]
 
     def move(self, xempty):
         """Given inputs, take move and return outputs.
@@ -24,7 +25,7 @@ def move(self, xempty):
         """
         output = dict()
         if self.state == 0:
-            if (xempty == 1):
+            if xempty == 1:
                 self.state = 1
 
                 output["xcar"] = 2
@@ -32,7 +33,7 @@ def move(self, xempty):
             else:
                 self._error(xempty)
         elif self.state == 1:
-            if (xempty == 1):
+            if xempty == 1:
                 self.state = 2
 
                 output["xcar"] = 4
@@ -40,7 +41,7 @@ def move(self, xempty):
             else:
                 self._error(xempty)
         elif self.state == 2:
-            if (xempty == 1):
+            if xempty == 1:
                 self.state = 3
 
                 output["xcar"] = 6
@@ -48,7 +49,7 @@ def move(self, xempty):
             else:
                 self._error(xempty)
         elif self.state == 3:
-            if (xempty == 1):
+            if xempty == 1:
                 self.state = 3
 
                 output["xcar"] = 6
@@ -56,7 +57,7 @@ def move(self, xempty):
             else:
                 self._error(xempty)
         elif self.state == 4:
-            if (xempty == 1):
+            if xempty == 1:
                 self.state = 0
 
                 output["xcar"] = 1
@@ -68,6 +69,4 @@ def move(self, xempty):
         return output
 
     def _error(self, xempty):
-        raise ValueError("Unrecognized input: " + (
-            "xempty = {xempty}; ").format(
-                xempty=xempty))
+        raise ValueError("Unrecognized input: " + ("xempty = {xempty}; ").format(xempty=xempty))
diff --git a/case_studies/evaluating_perception/lower_bounds.ipynb b/case_studies/evaluating_perception/lower_bounds.ipynb
index 16075c37..f0144d93 100644
--- a/case_studies/evaluating_perception/lower_bounds.ipynb
+++ b/case_studies/evaluating_perception/lower_bounds.ipynb
@@ -30,14 +30,10 @@
    ],
    "source": [
     "# Import libraries\n",
-    "from pacti.iocontract.utils import getVarlist\n",
+    "from case_study import *\n",
     "from PIL import Image\n",
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
-    "import pdb\n",
-    "from case_study import * \n",
-    "import random\n",
-    "import pickle as pkl\n",
-    "import collections"
+    "\n",
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -56,15 +52,15 @@
    "outputs": [],
    "source": [
     "TILESIZE = 50\n",
-    "ORIENTATIONS = {'n': 270, 'e': 0, 's': 90,'w':180, 'ne':315, 'nw':225, 'se':45, 'sw':135}\n",
+    "ORIENTATIONS = {\"n\": 270, \"e\": 0, \"s\": 90, \"w\": 180, \"ne\": 315, \"nw\": 225, \"se\": 45, \"sw\": 135}\n",
     "START_CROSSWALK = -1\n",
     "END_CROSSWALK = 2\n",
     "CROSSWALK_V = 1\n",
     "CROSSWALK_LOCATIONS = dict()\n",
     "ped_loc = []\n",
-    "for i, num in enumerate(range(2*START_CROSSWALK,2*(END_CROSSWALK+1))):\n",
-    "    CROSSWALK_LOCATIONS.update({i: (num/2, CROSSWALK_V)})\n",
-    "    ped_loc.append(num/2)\n"
+    "for i, num in enumerate(range(2 * START_CROSSWALK, 2 * (END_CROSSWALK + 1))):\n",
+    "    CROSSWALK_LOCATIONS.update({i: (num / 2, CROSSWALK_V)})\n",
+    "    ped_loc.append(num / 2)"
    ]
   },
   {
@@ -94,23 +90,23 @@
     }
    ],
    "source": [
-    "class GridCar():\n",
+    "class GridCar:\n",
     "    def __init__(self):\n",
     "        self.xc_init = 0\n",
     "        self.vc_init = 0\n",
     "        self.ped = 1\n",
     "        self.N = 10\n",
     "        self.Vmax = 2\n",
-    "        self.xped = 10-CROSSWALK_V\n",
+    "        self.xped = 10 - CROSSWALK_V\n",
     "        self.main_dir = os.path.dirname(os.path.dirname(os.path.realpath(\"__file__\")))\n",
-    "        self.car_fig = self.main_dir + '/evaluating_perception/imglib/red_car.png'\n",
-    "        self.ped_fig = self.main_dir + '/evaluating_perception/imglib/pedestrian_img.png'\n",
+    "        self.car_fig = self.main_dir + \"/evaluating_perception/imglib/red_car.png\"\n",
+    "        self.ped_fig = self.main_dir + \"/evaluating_perception/imglib/pedestrian_img.png\"\n",
     "        self.car_data = [9, 0]\n",
     "        if self.ped == 1:\n",
     "            self.ped_data = [CROSSWALK_V, min(ped_loc)]\n",
     "        else:\n",
     "            self.ped_data = None\n",
-    "    \n",
+    "\n",
     "    def dynamics(self):\n",
     "        transitions = dict()\n",
     "        for x in range(0, self.N):\n",
@@ -119,7 +115,7 @@
     "                x_succ.append(x + v)\n",
     "            transitions[x] = x_succ\n",
     "        return transitions\n",
-    "    \n",
+    "\n",
     "    def plot_grid(self):\n",
     "        size = [1, self.N]\n",
     "        x_min = 0\n",
@@ -127,9 +123,9 @@
     "        y_min = 0\n",
     "        y_max = size[1] * TILESIZE\n",
     "        # x_min, x_max, y_min, y_max = get_map_corners(map)\n",
-    "        \n",
+    "\n",
     "        fig, ax = plt.subplots()\n",
-    "        ax.axis('equal')\n",
+    "        ax.axis(\"equal\")\n",
     "        ax.set_xlim(x_min, x_max)\n",
     "        ax.set_ylim(y_min, y_max)\n",
     "        ax.xaxis.set_visible(False)\n",
@@ -137,12 +133,14 @@
     "\n",
     "        # fill in the road regions\n",
     "        road_tiles = []\n",
-    "        width_tiles = np.arange(0,size[0]+1)*2*TILESIZE\n",
-    "        lanes_tiles = np.arange(0,size[1]+1)*TILESIZE\n",
-    "        \n",
-    "        for i in np.arange(0,size[0]):\n",
-    "            for k in np.arange(0,size[1]+1):\n",
-    "                tile = patches.Rectangle((width_tiles[i],lanes_tiles[k]),2*TILESIZE,TILESIZE,linewidth=1,facecolor='k', alpha=0.4)\n",
+    "        width_tiles = np.arange(0, size[0] + 1) * 2 * TILESIZE\n",
+    "        lanes_tiles = np.arange(0, size[1] + 1) * TILESIZE\n",
+    "\n",
+    "        for i in np.arange(0, size[0]):\n",
+    "            for k in np.arange(0, size[1] + 1):\n",
+    "                tile = patches.Rectangle(\n",
+    "                    (width_tiles[i], lanes_tiles[k]), 2 * TILESIZE, TILESIZE, linewidth=1, facecolor=\"k\", alpha=0.4\n",
+    "                )\n",
     "                road_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(road_tiles, match_original=True))\n",
     "\n",
@@ -150,76 +148,79 @@
     "        crosswalk_tiles = []\n",
     "        for item in CROSSWALK_LOCATIONS.keys():\n",
     "            if item % 2 == 0:\n",
-    "                color = 'silver'\n",
+    "                color = \"silver\"\n",
     "                alpha = 0.5\n",
     "            else:\n",
-    "                color = 'k'\n",
+    "                color = \"k\"\n",
     "                alpha = 0.5\n",
-    "            width = CROSSWALK_LOCATIONS[item][0]*TILESIZE\n",
-    "            lanes = CROSSWALK_LOCATIONS[item][1]*TILESIZE\n",
-    "            tile = patches.Rectangle((width,lanes),TILESIZE/2,TILESIZE,linewidth=1,facecolor=color, alpha=alpha)\n",
+    "            width = CROSSWALK_LOCATIONS[item][0] * TILESIZE\n",
+    "            lanes = CROSSWALK_LOCATIONS[item][1] * TILESIZE\n",
+    "            tile = patches.Rectangle((width, lanes), TILESIZE / 2, TILESIZE, linewidth=1, facecolor=color, alpha=alpha)\n",
     "            crosswalk_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(crosswalk_tiles, match_original=True))\n",
     "        # TODO: Add cross-walk tile names next to the cells\n",
     "        plt.gca().invert_yaxis()\n",
     "        return ax\n",
-    "    \n",
-    "    \n",
+    "\n",
     "    def plot_car(self, ax):\n",
     "        y_tile, speed = self.car_data\n",
-    "        theta_d = 270 # Rotate car to face north\n",
-    "        x = (1) * TILESIZE/2\n",
+    "        theta_d = 270  # Rotate car to face north\n",
+    "        x = (1) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        car_fig = Image.open(self.car_fig)\n",
     "        car_fig = car_fig.rotate(theta_d, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(car_fig, zorder=1, interpolation='bilinear', extent=[x+2, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            car_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 2, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    def plot_ped(self, ax):\n",
     "        y_tile, x_tile = self.ped_data\n",
-    "        x = (x_tile) * TILESIZE/2\n",
+    "        x = (x_tile) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        ped_fig = Image.open(self.ped_fig)\n",
     "        ped_fig = ped_fig.rotate(180, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(ped_fig, zorder=1, interpolation='bilinear', extent=[x+4, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            ped_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 4, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    # Function to calculate all system states:\n",
     "    def construct_transitions(self):\n",
     "        T = dict()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
     "                st = (xcar, vcar)\n",
     "                end_st = []\n",
     "                if xcar == self.N:\n",
     "                    end_st.append((xcar, vcar))\n",
     "                elif vcar == 0:\n",
-    "                    xcar_p = min(self.N, xcar+1)\n",
+    "                    xcar_p = min(self.N, xcar + 1)\n",
     "                    end_st.append((xcar, vcar))\n",
-    "                    end_st.append((xcar_p, vcar+1))\n",
-    "                    end_st.append((xcar, vcar+1))\n",
+    "                    end_st.append((xcar_p, vcar + 1))\n",
+    "                    end_st.append((xcar, vcar + 1))\n",
     "                else:\n",
-    "                    xcar_p = min(self.N, xcar+vcar)\n",
+    "                    xcar_p = min(self.N, xcar + vcar)\n",
     "                    end_st.append((xcar_p, vcar))\n",
-    "                    end_st.append((xcar_p, vcar-1))\n",
+    "                    end_st.append((xcar_p, vcar - 1))\n",
     "                    if vcar < Vhigh:\n",
-    "                        end_st.append((xcar_p, vcar+1))\n",
+    "                        end_st.append((xcar_p, vcar + 1))\n",
     "                T[st] = end_st\n",
     "        return T\n",
-    "    \n",
+    "\n",
     "    # System states for pedestrian\n",
     "    def system_states_example_ped(self):\n",
-    "        nS = self.N*(self.Vmax+1)\n",
-    "        state = lambda x,v: (self.Vmax+1)*(x-1) + v\n",
+    "        nS = self.N * (self.Vmax + 1)\n",
+    "        state = lambda x, v: (self.Vmax + 1) * (x - 1) + v\n",
     "        state_to_S = dict()\n",
     "        S = set()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
-    "                st = \"S\"+str(state(xcar, vcar))\n",
-    "                state_to_S[xcar,vcar] = st\n",
-    "                S|={st}\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
+    "                st = \"S\" + str(state(xcar, vcar))\n",
+    "                state_to_S[xcar, vcar] = st\n",
+    "                S |= {st}\n",
     "        T = self.construct_transitions()\n",
     "        return S, state_to_S, T\n",
     "\n",
@@ -267,7 +268,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "ubounds_ped, lbounds_ped, points_ped, tpped_vals = derive_prob_bounds(fn, true_env)\n",
     "# plot_probabilities_bounds(points_ped, tpped_vals, true_env, vmax, Ncar, ubounds_ped, lbounds_ped, rand =True)\n",
@@ -283,7 +284,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "ubounds_obj, lbounds_obj, points_obj, tpobj_vals = derive_prob_bounds(fn, true_env)\n",
     "# plot_probabilities_bounds(points_obj, tpobj_vals, true_env, vmax, Ncar, ubounds_obj, lbounds_obj, rand =True)\n",
@@ -299,7 +300,7 @@
     "\n",
     "# Derive bounds and plot probability bounds:\n",
     "else:\n",
-    "    fn = \"data/points_\"+true_env+\"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
+    "    fn = \"data/points_\" + true_env + \"_lb0.6_ub0.999_vmax2_N6rand.pkl\"\n",
     "\n",
     "ubounds_emp, lbounds_emp, points_emp, tpemp_vals = derive_prob_bounds(fn, true_env)\n",
     "# plot_probabilities_bounds(points_emp, tpemp_vals, true_env, vmax, Ncar, ubounds_emp, lbounds_emp, rand =True)"
@@ -349,74 +350,56 @@
     "## Control contract for pedestrian class\n",
     "def control_contract_ped(lbounds_ped):\n",
     "    lb_m, lb_c = lbounds_ped\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\"],\n",
+    "        \"output_vars\": [\"P_ped\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_ped\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"tp_ped\": -1}, \"constant\": -0.6},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_ped\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"tp_ped\":-1},\n",
-    "            \"constant\":-0.6}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"tp_ped\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P_ped\": -1, \"tp_ped\": lb_m}, \"constant\": -1 * lb_c}],\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_obj(lbounds_obj):\n",
     "    lb_m, lb_c = lbounds_obj\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_obj\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_obj\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_obj\"],\n",
+    "        \"output_vars\": [\"P_obj\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_obj\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"tp_obj\": -1}, \"constant\": -0.3},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_obj\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"tp_obj\":-1},\n",
-    "            \"constant\":-0.3}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_obj\":-1, \"tp_obj\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P_obj\": -1, \"tp_obj\": lb_m}, \"constant\": -1 * lb_c}],\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_emp(lbounds_emp):\n",
     "    lb_m, lb_c = lbounds_emp\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_emp\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"tp_emp\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_emp\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"tp_emp\": -1}, \"constant\": -0.6},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_emp\":-1, \"tp_emp\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P_emp\": -1, \"tp_emp\": lb_m}, \"constant\": -1 * lb_c}],\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cped = PolyhedralContract.from_dict(control_contract_ped(lbounds_ped))\n",
     "Cobj = PolyhedralContract.from_dict(control_contract_obj(lbounds_obj))\n",
     "Cemp = PolyhedralContract.from_dict(control_contract_emp(lbounds_emp))\n",
     "\n",
     "print(\"Controller Contract for pedestrian class:\\n\" + str(Cped) + \"\\n\")\n",
-    "print(\"Controller Contract for object class:\\n\" + str(Cobj)+ \"\\n\")\n",
-    "print(\"Controller Contract for empty class:\\n\" + str(Cemp)+ \"\\n\")"
+    "print(\"Controller Contract for object class:\\n\" + str(Cobj) + \"\\n\")\n",
+    "print(\"Controller Contract for empty class:\\n\" + str(Cemp) + \"\\n\")"
    ]
   },
   {
@@ -448,7 +431,7 @@
    ],
    "source": [
     "# Construct merger of contracts:\n",
-    "C_controller = Cped.compose(Cobj) \n",
+    "C_controller = Cped.compose(Cobj)\n",
     "C_controller = C_controller.compose(Cemp)\n",
     "\n",
     "print(\"Merged Controller Contract for all object classes:\\n\" + str(C_controller) + \"\\n\")"
@@ -493,34 +476,24 @@
     "## System contract: P >= 0.99 for each class\n",
     "def system_contract():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"x_e\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "        \"input_vars\": [\"x_e\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"x_e\": -1}, \"constant\": -1}, {\"coefficients\": {\"x_e\": 1}, \"constant\": 1}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1}, \"constant\": -0.99},\n",
+    "            #             {\"coefficients\":{\"P_ped\":1},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1}, \"constant\": -0.80},\n",
+    "            #             {\"coefficients\":{\"P_obj\":1},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1}, \"constant\": -0.95},\n",
+    "            #             {\"coefficients\":{\"P_emp\":1},\n",
+    "            #             \"constant\":1}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"x_e\":-1}, \"constant\":-1},\n",
-    "            {\"coefficients\":{\"x_e\":1}, \"constant\":1}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1},\n",
-    "            \"constant\":-0.99},\n",
-    "#             {\"coefficients\":{\"P_ped\":1},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1},\n",
-    "            \"constant\":-0.80},\n",
-    "#             {\"coefficients\":{\"P_obj\":1},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1},\n",
-    "            \"constant\":-0.95},\n",
-    "#             {\"coefficients\":{\"P_emp\":1},\n",
-    "#             \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys = PolyhedralContract.from_dict(system_contract())\n",
     "print(\"System Contract (numerical bounds):\\n\" + str(Csys) + \"\\n\")"
    ]
@@ -547,7 +520,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_numerical = Csys.quotient(C_controller)\n",
-    "print(\"Quotient Contract with numerical bounds on true positive rates:\\n\" + str(C_numerical)+\"\\n\")"
+    "print(\"Quotient Contract with numerical bounds on true positive rates:\\n\" + str(C_numerical) + \"\\n\")"
    ]
   },
   {
@@ -568,7 +541,7 @@
     }
    ],
    "source": [
-    "-0.150300000000000/-0.200500000000000"
+    "-0.150300000000000 / -0.200500000000000"
    ]
   },
   {
@@ -602,34 +575,24 @@
     "## System contract: P >= 0.99\n",
     "def system_contract_dist():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"d\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "        \"input_vars\": [\"d\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"d\": -1}, \"constant\": 0}, {\"coefficients\": {\"d\": 1}, \"constant\": 10}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"d\": -0.099}, \"constant\": -0.99},\n",
+    "            #             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"d\": -0.08}, \"constant\": -0.8},\n",
+    "            #             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"d\": -0.095}, \"constant\": -0.95},\n",
+    "            #             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
+    "            #             \"constant\":1}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"d\":-1}, \"constant\":0},\n",
-    "            {\"coefficients\":{\"d\":1}, \"constant\":10}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1, \"d\":-0.099},\n",
-    "            \"constant\":-0.99},\n",
-    "#             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1, \"d\":-0.08},\n",
-    "            \"constant\":-0.8},\n",
-    "#             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1, \"d\":-0.095},\n",
-    "            \"constant\":-0.95},\n",
-    "#             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
-    "#             \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys_dist = PolyhedralContract.from_dict(system_contract_dist())\n",
     "print(\"System Contract (distance bounds):\\n\" + str(Csys_dist) + \"\\n\")"
    ]
@@ -656,7 +619,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_dist = Csys_dist.quotient(C_controller)\n",
-    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist)+\"\\n\")"
+    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist) + \"\\n\")"
    ]
   },
   {
@@ -677,7 +640,7 @@
     }
    ],
    "source": [
-    " 0.15030000000/0.200500000000000"
+    "0.15030000000 / 0.200500000000000"
    ]
   },
   {
diff --git a/case_studies/evaluating_perception/not_ped_controller.py b/case_studies/evaluating_perception/not_ped_controller.py
index 7d324d5d..9ee30111 100644
--- a/case_studies/evaluating_perception/not_ped_controller.py
+++ b/case_studies/evaluating_perception/not_ped_controller.py
@@ -11,9 +11,10 @@ class TulipStrategy(object):
     Automatically generated by tulip.dumpsmach on 2023-01-22 06:48:42 UTC
     To learn more about TuLiP, visit http://tulip-control.org
     """
+
     def __init__(self):
         self.state = 6
-        self.input_vars = ['xobj']
+        self.input_vars = ["xobj"]
 
     def move(self, xobj):
         """Given inputs, take move and return outputs.
@@ -24,7 +25,7 @@ def move(self, xobj):
         """
         output = dict()
         if self.state == 0:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 1
 
                 output["xcar"] = 2
@@ -32,7 +33,7 @@ def move(self, xobj):
             else:
                 self._error(xobj)
         elif self.state == 1:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 2
 
                 output["xcar"] = 3
@@ -40,7 +41,7 @@ def move(self, xobj):
             else:
                 self._error(xobj)
         elif self.state == 2:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 3
 
                 output["xcar"] = 4
@@ -48,7 +49,7 @@ def move(self, xobj):
             else:
                 self._error(xobj)
         elif self.state == 3:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 4
 
                 output["xcar"] = 5
@@ -56,7 +57,7 @@ def move(self, xobj):
             else:
                 self._error(xobj)
         elif self.state == 4:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 5
 
                 output["xcar"] = 6
@@ -64,7 +65,7 @@ def move(self, xobj):
             else:
                 self._error(xobj)
         elif self.state == 5:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 5
 
                 output["xcar"] = 6
@@ -72,7 +73,7 @@ def move(self, xobj):
             else:
                 self._error(xobj)
         elif self.state == 6:
-            if (xobj == 1):
+            if xobj == 1:
                 self.state = 0
 
                 output["xcar"] = 1
@@ -84,6 +85,4 @@ def move(self, xobj):
         return output
 
     def _error(self, xobj):
-        raise ValueError("Unrecognized input: " + (
-            "xobj = {xobj}; ").format(
-                xobj=xobj))
+        raise ValueError("Unrecognized input: " + ("xobj = {xobj}; ").format(xobj=xobj))
diff --git a/case_studies/evaluating_perception/ped_controller.py b/case_studies/evaluating_perception/ped_controller.py
index 971de52f..9396d9d6 100644
--- a/case_studies/evaluating_perception/ped_controller.py
+++ b/case_studies/evaluating_perception/ped_controller.py
@@ -11,9 +11,10 @@ class TulipStrategy(object):
     Automatically generated by tulip.dumpsmach on 2023-01-22 06:48:42 UTC
     To learn more about TuLiP, visit http://tulip-control.org
     """
+
     def __init__(self):
         self.state = 8
-        self.input_vars = ['xped']
+        self.input_vars = ["xped"]
 
     def move(self, xped):
         """Given inputs, take move and return outputs.
@@ -24,7 +25,7 @@ def move(self, xped):
         """
         output = dict()
         if self.state == 0:
-            if (xped == 0):
+            if xped == 0:
                 self.state = 5
 
                 output["xcar"] = 2
@@ -32,7 +33,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 1:
-            if (xped == 1):
+            if xped == 1:
                 self.state = 2
 
                 output["xcar"] = 2
@@ -40,7 +41,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 2:
-            if (xped == 1):
+            if xped == 1:
                 self.state = 3
 
                 output["xcar"] = 3
@@ -48,7 +49,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 3:
-            if (xped == 1):
+            if xped == 1:
                 self.state = 4
 
                 output["xcar"] = 4
@@ -56,7 +57,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 4:
-            if (xped == 1):
+            if xped == 1:
                 self.state = 4
 
                 output["xcar"] = 4
@@ -64,7 +65,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 5:
-            if (xped == 0):
+            if xped == 0:
                 self.state = 6
 
                 output["xcar"] = 4
@@ -72,7 +73,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 6:
-            if (xped == 0):
+            if xped == 0:
                 self.state = 7
 
                 output["xcar"] = 6
@@ -80,7 +81,7 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 7:
-            if (xped == 0):
+            if xped == 0:
                 self.state = 7
 
                 output["xcar"] = 6
@@ -88,12 +89,12 @@ def move(self, xped):
             else:
                 self._error(xped)
         elif self.state == 8:
-            if (xped == 0):
+            if xped == 0:
                 self.state = 0
 
                 output["xcar"] = 1
                 output["vcar"] = 1
-            elif (xped == 1):
+            elif xped == 1:
                 self.state = 1
 
                 output["xcar"] = 1
@@ -105,6 +106,4 @@ def move(self, xped):
         return output
 
     def _error(self, xped):
-        raise ValueError("Unrecognized input: " + (
-            "xped = {xped}; ").format(
-                xped=xped))
+        raise ValueError("Unrecognized input: " + ("xped = {xped}; ").format(xped=xped))
diff --git a/case_studies/evaluating_perception/recompute.ipynb b/case_studies/evaluating_perception/recompute.ipynb
index 81a82118..bcb29a21 100644
--- a/case_studies/evaluating_perception/recompute.ipynb
+++ b/case_studies/evaluating_perception/recompute.ipynb
@@ -31,15 +31,13 @@
    ],
    "source": [
     "# Import libraries\n",
-    "from pacti.iocontract.utils import getVarlist\n",
-    "from PIL import Image\n",
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
-    "import pdb\n",
-    "from case_study import * \n",
-    "import random\n",
-    "import pickle as pkl\n",
     "import os\n",
-    "import collections"
+    "import pdb\n",
+    "\n",
+    "from case_study import *\n",
+    "from PIL import Image\n",
+    "\n",
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -58,8 +56,8 @@
    "outputs": [],
    "source": [
     "# Parameters of car / pedestrian example:\n",
-    "N = 10 # number of cells in the grid, and the crosswalk is located one cell before the end of the lane.\n",
-    "Vmax = 3 # Maximum speed of car\n"
+    "N = 10  # number of cells in the grid, and the crosswalk is located one cell before the end of the lane.\n",
+    "Vmax = 3  # Maximum speed of car"
    ]
   },
   {
@@ -90,42 +88,43 @@
    ],
    "source": [
     "TILESIZE = 50\n",
-    "ORIENTATIONS = {'n': 270, 'e': 0, 's': 90,'w':180, 'ne':315, 'nw':225, 'se':45, 'sw':135}\n",
+    "ORIENTATIONS = {\"n\": 270, \"e\": 0, \"s\": 90, \"w\": 180, \"ne\": 315, \"nw\": 225, \"se\": 45, \"sw\": 135}\n",
     "START_CROSSWALK = -1\n",
     "END_CROSSWALK = 2\n",
     "CROSSWALK_POS = 1\n",
     "CROSSWALK_LOCATIONS = dict()\n",
     "ped_loc = []\n",
-    "for i, num in enumerate(range(2*START_CROSSWALK,2*(END_CROSSWALK+1))):\n",
-    "    CROSSWALK_LOCATIONS.update({i: (num/2, CROSSWALK_POS)})\n",
-    "    ped_loc.append(num/2)\n",
-    "    \n",
-    "class GridCar():\n",
+    "for i, num in enumerate(range(2 * START_CROSSWALK, 2 * (END_CROSSWALK + 1))):\n",
+    "    CROSSWALK_LOCATIONS.update({i: (num / 2, CROSSWALK_POS)})\n",
+    "    ped_loc.append(num / 2)\n",
+    "\n",
+    "\n",
+    "class GridCar:\n",
     "    def __init__(self, N, Vmax):\n",
     "        self.xc_init = 1\n",
     "        self.vc_init = 1\n",
     "        self.ped = 1\n",
     "        self.N = N\n",
     "        self.Vmax = Vmax\n",
-    "        self.xped = self.N-CROSSWALK_POS\n",
+    "        self.xped = self.N - CROSSWALK_POS\n",
     "        self.main_dir = os.path.dirname(os.path.dirname(os.path.realpath(\"__file__\")))\n",
-    "        self.car_fig = self.main_dir + '/evaluating_perception/imglib/red_car.png'\n",
-    "        self.ped_fig = self.main_dir + '/evaluating_perception/imglib/pedestrian_img.png'\n",
+    "        self.car_fig = self.main_dir + \"/evaluating_perception/imglib/red_car.png\"\n",
+    "        self.ped_fig = self.main_dir + \"/evaluating_perception/imglib/pedestrian_img.png\"\n",
     "        self.car_data = [9, 0]\n",
     "        if self.ped == 1:\n",
     "            self.ped_data = [CROSSWALK_POS, min(ped_loc)]\n",
     "        else:\n",
     "            self.ped_data = None\n",
-    "    \n",
+    "\n",
     "    def dynamics(self):\n",
     "        transitions = dict()\n",
-    "        for x in range(1, self.N+1):\n",
+    "        for x in range(1, self.N + 1):\n",
     "            x_succ = []\n",
     "            for v in range(0, self.Vmax):\n",
     "                x_succ.append(x + v)\n",
     "            transitions[x] = x_succ\n",
     "        return transitions\n",
-    "    \n",
+    "\n",
     "    def plot_grid(self):\n",
     "        size = [1, self.N]\n",
     "        x_min = 0\n",
@@ -133,9 +132,9 @@
     "        y_min = 0\n",
     "        y_max = size[1] * TILESIZE\n",
     "        # x_min, x_max, y_min, y_max = get_map_corners(map)\n",
-    "        \n",
+    "\n",
     "        fig, ax = plt.subplots()\n",
-    "        ax.axis('equal')\n",
+    "        ax.axis(\"equal\")\n",
     "        ax.set_xlim(x_min, x_max)\n",
     "        ax.set_ylim(y_min, y_max)\n",
     "        ax.xaxis.set_visible(False)\n",
@@ -143,12 +142,14 @@
     "\n",
     "        # fill in the road regions\n",
     "        road_tiles = []\n",
-    "        width_tiles = np.arange(0,size[0]+1)*2*TILESIZE\n",
-    "        lanes_tiles = np.arange(0,size[1]+1)*TILESIZE\n",
-    "        \n",
-    "        for i in np.arange(0,size[0]):\n",
-    "            for k in np.arange(0,size[1]+1):\n",
-    "                tile = patches.Rectangle((width_tiles[i],lanes_tiles[k]),2*TILESIZE,TILESIZE,linewidth=1,facecolor='k', alpha=0.4)\n",
+    "        width_tiles = np.arange(0, size[0] + 1) * 2 * TILESIZE\n",
+    "        lanes_tiles = np.arange(0, size[1] + 1) * TILESIZE\n",
+    "\n",
+    "        for i in np.arange(0, size[0]):\n",
+    "            for k in np.arange(0, size[1] + 1):\n",
+    "                tile = patches.Rectangle(\n",
+    "                    (width_tiles[i], lanes_tiles[k]), 2 * TILESIZE, TILESIZE, linewidth=1, facecolor=\"k\", alpha=0.4\n",
+    "                )\n",
     "                road_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(road_tiles, match_original=True))\n",
     "\n",
@@ -156,76 +157,79 @@
     "        crosswalk_tiles = []\n",
     "        for item in CROSSWALK_LOCATIONS.keys():\n",
     "            if item % 2 == 0:\n",
-    "                color = 'silver'\n",
+    "                color = \"silver\"\n",
     "                alpha = 0.5\n",
     "            else:\n",
-    "                color = 'k'\n",
+    "                color = \"k\"\n",
     "                alpha = 0.5\n",
-    "            width = CROSSWALK_LOCATIONS[item][0]*TILESIZE\n",
-    "            lanes = CROSSWALK_LOCATIONS[item][1]*TILESIZE\n",
-    "            tile = patches.Rectangle((width,lanes),TILESIZE/2,TILESIZE,linewidth=1,facecolor=color, alpha=alpha)\n",
+    "            width = CROSSWALK_LOCATIONS[item][0] * TILESIZE\n",
+    "            lanes = CROSSWALK_LOCATIONS[item][1] * TILESIZE\n",
+    "            tile = patches.Rectangle((width, lanes), TILESIZE / 2, TILESIZE, linewidth=1, facecolor=color, alpha=alpha)\n",
     "            crosswalk_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(crosswalk_tiles, match_original=True))\n",
     "        # TODO: Add cross-walk tile names next to the cells\n",
     "        plt.gca().invert_yaxis()\n",
     "        return ax\n",
-    "    \n",
-    "    \n",
+    "\n",
     "    def plot_car(self, ax):\n",
     "        y_tile, speed = self.car_data\n",
-    "        theta_d = 270 # Rotate car to face north\n",
-    "        x = (1) * TILESIZE/2\n",
+    "        theta_d = 270  # Rotate car to face north\n",
+    "        x = (1) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        car_fig = Image.open(self.car_fig)\n",
     "        car_fig = car_fig.rotate(theta_d, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(car_fig, zorder=1, interpolation='bilinear', extent=[x+2, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            car_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 2, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    def plot_ped(self, ax):\n",
     "        y_tile, x_tile = self.ped_data\n",
-    "        x = (x_tile) * TILESIZE/2\n",
+    "        x = (x_tile) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        ped_fig = Image.open(self.ped_fig)\n",
     "        ped_fig = ped_fig.rotate(180, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(ped_fig, zorder=1, interpolation='bilinear', extent=[x+4, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            ped_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 4, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    # Function to calculate all system states:\n",
     "    def construct_transitions(self):\n",
     "        T = dict()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
     "                st = (xcar, vcar)\n",
     "                end_st = []\n",
     "                if xcar == self.N:\n",
     "                    end_st.append((xcar, vcar))\n",
     "                elif vcar == 0:\n",
-    "                    xcar_p = min(self.N, xcar+1)\n",
+    "                    xcar_p = min(self.N, xcar + 1)\n",
     "                    end_st.append((xcar, vcar))\n",
-    "                    end_st.append((xcar_p, vcar+1))\n",
-    "                    end_st.append((xcar, vcar+1))\n",
+    "                    end_st.append((xcar_p, vcar + 1))\n",
+    "                    end_st.append((xcar, vcar + 1))\n",
     "                else:\n",
-    "                    xcar_p = min(self.N, xcar+vcar)\n",
+    "                    xcar_p = min(self.N, xcar + vcar)\n",
     "                    end_st.append((xcar_p, vcar))\n",
-    "                    end_st.append((xcar_p, vcar-1))\n",
+    "                    end_st.append((xcar_p, vcar - 1))\n",
     "                    if vcar < Vhigh:\n",
-    "                        end_st.append((xcar_p, vcar+1))\n",
+    "                        end_st.append((xcar_p, vcar + 1))\n",
     "                T[st] = end_st\n",
     "        return T\n",
-    "    \n",
+    "\n",
     "    # System states for pedestrian\n",
     "    def system_states_example_ped(self):\n",
-    "        nS = self.N*(self.Vmax+1)\n",
-    "        state = lambda x,v: (self.Vmax+1)*(x-1) + v\n",
+    "        nS = self.N * (self.Vmax + 1)\n",
+    "        state = lambda x, v: (self.Vmax + 1) * (x - 1) + v\n",
     "        state_to_S = dict()\n",
     "        S = set()\n",
-    "        for xcar in range(1,self.N+1):\n",
-    "            for vcar in range(0, self.Vmax+1):\n",
-    "                st = \"S\"+str(state(xcar, vcar))\n",
-    "                state_to_S[xcar,vcar] = st\n",
-    "                S|={st}\n",
+    "        for xcar in range(1, self.N + 1):\n",
+    "            for vcar in range(0, self.Vmax + 1):\n",
+    "                st = \"S\" + str(state(xcar, vcar))\n",
+    "                state_to_S[xcar, vcar] = st\n",
+    "                S |= {st}\n",
     "        T = self.construct_transitions()\n",
     "        return S, state_to_S, T\n",
     "\n",
@@ -256,25 +260,25 @@
    "outputs": [],
    "source": [
     "def bound_probs_lp(tpx, proby):\n",
-    "    '''\n",
-    "    Finding the tightest lower bound using linear programming. Maximize the area under the lower bound such that \n",
+    "    \"\"\"\n",
+    "    Finding the tightest lower bound using linear programming. Maximize the area under the lower bound such that\n",
     "    for all sampled probabilities, the affine expression is a lower bound.\n",
-    "    '''\n",
+    "    \"\"\"\n",
     "    l = min(tpx)\n",
     "    u = max(tpx)\n",
     "    m = cp.Variable()\n",
     "    b = cp.Variable()\n",
     "\n",
-    "    alpha = (u**2 - l**2)/2\n",
-    "    beta = (u-l)\n",
-    "    obj =  alpha*m + beta*b\n",
+    "    alpha = (u**2 - l**2) / 2\n",
+    "    beta = u - l\n",
+    "    obj = alpha * m + beta * b\n",
     "    n = len(tpx)\n",
     "\n",
-    "    A = np.array(tpx).reshape(1,-1)\n",
-    "    B = np.ones((n,1))\n",
-    "    C = np.array(proby).reshape(1,-1)\n",
+    "    A = np.array(tpx).reshape(1, -1)\n",
+    "    B = np.ones((n, 1))\n",
+    "    C = np.array(proby).reshape(1, -1)\n",
     "\n",
-    "    constraints = [A*m + B*b <=C]\n",
+    "    constraints = [A * m + B * b <= C]\n",
     "    prob = cp.Problem(cp.Maximize(obj), constraints)\n",
     "    prob.solve()\n",
     "    if prob.status == \"infeasible\" or prob.status == \"unbounded\":\n",
@@ -287,7 +291,7 @@
     "\n",
     "def derive_prob_bounds_lp(fn, true_env):\n",
     "    points, tp_vals = load_result(fn)\n",
-    "    result_tuple = [[tp_vals[i],points[i]] for i in range(len(tp_vals))]\n",
+    "    result_tuple = [[tp_vals[i], points[i]] for i in range(len(tp_vals))]\n",
     "    if true_env == \"ped\":\n",
     "        ub, lb = bound_probs_lp(tp_vals, points)\n",
     "    elif true_env == \"obj\":\n",
@@ -336,23 +340,24 @@
     "# Plotting Probabilities:\n",
     "def plot_probabilities_bounds(points, tp_vals, ubounds, lbounds, true_env):\n",
     "    fig, ax = plt.subplots()\n",
-    "    ax.tick_params(axis='both', which='major', labelsize=10)\n",
-    "    plt.plot(tp_vals, points, 'b*',label='sampled')\n",
+    "    ax.tick_params(axis=\"both\", which=\"major\", labelsize=10)\n",
+    "    plt.plot(tp_vals, points, \"b*\", label=\"sampled\")\n",
     "    if ubounds != []:\n",
     "        ub_m, ub_c = ubounds\n",
-    "        y_ub = ub_m*np.array(tp_vals) + ub_c\n",
-    "        plt.plot(tp_vals, y_ub, 'r')\n",
-    "        \n",
+    "        y_ub = ub_m * np.array(tp_vals) + ub_c\n",
+    "        plt.plot(tp_vals, y_ub, \"r\")\n",
+    "\n",
     "    lb_m, lb_c = lbounds\n",
-    "    y_lb = lb_m*np.array(tp_vals) + lb_c\n",
-    "    plt.plot(tp_vals, y_lb, 'k',label='lower bound',linewidth=4)\n",
+    "    y_lb = lb_m * np.array(tp_vals) + lb_c\n",
+    "    plt.plot(tp_vals, y_lb, \"k\", label=\"lower bound\", linewidth=4)\n",
     "    lb = min(tp_vals)\n",
     "    ub = max(tp_vals)\n",
-    "    plt.legend(prop={'size': 10})\n",
-    "    \n",
-    "    plt.title('Lower bound of $\\mathbb{P}_{'+true_env+'}$ as a function of $\\mathtt{TP}_{'+true_env+'}$.')\n",
-    "    plt.ylabel('$\\mathbb{P}_{'+true_env+'}$')\n",
-    "    plt.xlabel('$\\mathtt{TP}_{'+true_env+'}$')\n",
+    "    plt.legend(prop={\"size\": 10})\n",
+    "\n",
+    "    plt.title(\"Lower bound of $\\mathbb{P}_{\" + true_env + \"}$ as a function of $\\mathtt{TP}_{\" + true_env + \"}$.\")\n",
+    "    plt.ylabel(\"$\\mathbb{P}_{\" + true_env + \"}$\")\n",
+    "    plt.xlabel(\"$\\mathtt{TP}_{\" + true_env + \"}$\")\n",
+    "\n",
     "\n",
     "plot_probabilities_bounds(points_ped, tpped_vals, [], lbounds_ped, \"ped\")\n",
     "plot_probabilities_bounds(points_obj, tpobj_vals, [], lbounds_obj, \"obj\")\n",
@@ -404,78 +409,67 @@
     "## Control contract for pedestrian class\n",
     "def control_contract_ped(lbounds_ped):\n",
     "    lb_m, lb_c = lbounds_ped\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_ped\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"tp_ped\": -1}, \"constant\": -0.6}],\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"tp_ped\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_ped\":1, \"tp_ped\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"tp_ped\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_obj(lbounds_obj):\n",
     "    lb_m, lb_c = lbounds_obj\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            #             {\"coefficients\":{\"tp_obj\":1},\n",
+    "            #             \"constant\":0.999},\n",
+    "            {\"coefficients\": {\"tp_obj\": -1}, \"constant\": -0.3}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "#             {\"coefficients\":{\"tp_obj\":1},\n",
-    "#             \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_obj\":-1},\n",
-    "            \"constant\":-0.3}\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"tp_obj\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_obj\":1, \"tp_obj\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_obj\":-1, \"tp_obj\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_emp(lbounds_emp):\n",
     "    lb_m, lb_c = lbounds_emp\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\", \"tp_obj\", \"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            #             {\"coefficients\":{\"tp_emp\":1},\n",
+    "            #             \"constant\":0.999},\n",
+    "            {\"coefficients\": {\"tp_emp\": -1}, \"constant\": -0.6}\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "#             {\"coefficients\":{\"tp_emp\":1},\n",
-    "#             \"constant\":0.999},\n",
-    "            {\"coefficients\":{\"tp_emp\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "        \"guarantees\": [\n",
+    "            #           {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
+    "            #           \"constant\":ub_c},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"tp_emp\": lb_m}, \"constant\": -1 * lb_c}\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "#           {\"coefficients\":{\"P_emp\":1, \"tp_emp\":-1*ub_m},\n",
-    "#           \"constant\":ub_c},\n",
-    "          {\"coefficients\":{\"P_emp\":-1, \"tp_emp\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cped = PolyhedralContract.from_dict(control_contract_ped(lbounds_ped))\n",
     "Cobj = PolyhedralContract.from_dict(control_contract_obj(lbounds_obj))\n",
     "Cemp = PolyhedralContract.from_dict(control_contract_emp(lbounds_emp))\n",
     "\n",
     "print(\"Controller Contract for pedestrian class:\\n\" + str(Cped) + \"\\n\")\n",
-    "print(\"Controller Contract for object class:\\n\" + str(Cobj)+ \"\\n\")\n",
-    "print(\"Controller Contract for empty class:\\n\" + str(Cemp)+ \"\\n\")\n"
+    "print(\"Controller Contract for object class:\\n\" + str(Cobj) + \"\\n\")\n",
+    "print(\"Controller Contract for empty class:\\n\" + str(Cemp) + \"\\n\")"
    ]
   },
   {
@@ -499,7 +493,7 @@
    ],
    "source": [
     "# Construct merger of contracts:\n",
-    "C_controller = Cped.merge(Cobj) \n",
+    "C_controller = Cped.merge(Cobj)\n",
     "C_controller = C_controller.merge(Cemp)\n",
     "\n",
     "print(\"Merged Controller Contract for all object classes:\\n\" + str(C_controller) + \"\\n\")"
@@ -528,34 +522,24 @@
     "## System contract: P >= 0.99\n",
     "def system_contract_dist():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"d\"\n",
+    "        \"input_vars\": [\"d\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"d\": -1}, \"constant\": 0}, {\"coefficients\": {\"d\": 1}, \"constant\": 10}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"d\": -0.099}, \"constant\": -0.99},\n",
+    "            #             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"d\": -0.08}, \"constant\": -0.8},\n",
+    "            #             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
+    "            #             \"constant\":1},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"d\": -0.095}, \"constant\": -0.95},\n",
+    "            #             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
+    "            #             \"constant\":1}\n",
     "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"d\":-1}, \"constant\":0},\n",
-    "            {\"coefficients\":{\"d\":1}, \"constant\":10}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1, \"d\":-0.099},\n",
-    "            \"constant\":-0.99},\n",
-    "#             {\"coefficients\":{\"P_ped\":1, \"d\":0.00},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_obj\":-1, \"d\":-0.08},\n",
-    "            \"constant\":-0.8},\n",
-    "#             {\"coefficients\":{\"P_obj\":1, \"d\":0},\n",
-    "#             \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_emp\":-1, \"d\":-0.095},\n",
-    "            \"constant\":-0.95},\n",
-    "#             {\"coefficients\":{\"P_emp\":1, \"d\":0.0},\n",
-    "#             \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys_dist = PolyhedralContract.from_dict(system_contract_dist())\n",
     "print(\"System Contract (distance bounds):\\n\" + str(Csys_dist) + \"\\n\")"
    ]
@@ -582,7 +566,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_dist = Csys_dist.quotient(C_controller)\n",
-    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist)+\"\\n\")"
+    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist) + \"\\n\")"
    ]
   }
  ],
diff --git a/case_studies/evaluating_perception/saved_results.ipynb b/case_studies/evaluating_perception/saved_results.ipynb
index dbad408d..69cc1654 100644
--- a/case_studies/evaluating_perception/saved_results.ipynb
+++ b/case_studies/evaluating_perception/saved_results.ipynb
@@ -17,18 +17,14 @@
    "outputs": [],
    "source": [
     "# Import libraries\n",
-    "from pacti.iocontract.utils import getVarlist\n",
-    "from PIL import Image\n",
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
-    "import pdb\n",
-    "import random\n",
     "import pickle as pkl\n",
-    "import numpy as np\n",
-    "import collections\n",
-    "from IPython.display import display, Image\n",
-    "from IPython.display import Latex\n",
+    "\n",
     "import matplotlib.pyplot as plt\n",
-    "import matplotlib.image as mpimg"
+    "import numpy as np\n",
+    "from IPython.display import Image\n",
+    "from PIL import Image\n",
+    "\n",
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -148,23 +144,24 @@
     "# Plotting Probabilities:\n",
     "def plot_probabilities_bounds(points, tp_vals, ubounds, lbounds, true_env):\n",
     "    fig, ax = plt.subplots()\n",
-    "    ax.tick_params(axis='both', which='major', labelsize=10)\n",
-    "    plt.plot(tp_vals, points, 'b*',label='sampled')\n",
+    "    ax.tick_params(axis=\"both\", which=\"major\", labelsize=10)\n",
+    "    plt.plot(tp_vals, points, \"b*\", label=\"sampled\")\n",
     "    if ubounds != []:\n",
     "        ub_m, ub_c = ubounds\n",
-    "        y_ub = ub_m*np.array(tp_vals) + ub_c\n",
-    "        plt.plot(tp_vals, y_ub, 'r')\n",
-    "        \n",
+    "        y_ub = ub_m * np.array(tp_vals) + ub_c\n",
+    "        plt.plot(tp_vals, y_ub, \"r\")\n",
+    "\n",
     "    lb_m, lb_c = lbounds\n",
-    "    y_lb = lb_m*np.array(tp_vals) + lb_c\n",
-    "    plt.plot(tp_vals, y_lb, 'k',label='lower bound',linewidth=4)\n",
+    "    y_lb = lb_m * np.array(tp_vals) + lb_c\n",
+    "    plt.plot(tp_vals, y_lb, \"k\", label=\"lower bound\", linewidth=4)\n",
     "    lb = min(tp_vals)\n",
     "    ub = max(tp_vals)\n",
-    "    plt.legend(prop={'size': 10})\n",
-    "    \n",
-    "    plt.title('Lower bound of $\\mathbb{P}_{'+true_env+'}$ as a function of $\\mathtt{TP}_{'+true_env+'}$.')\n",
-    "    plt.ylabel('$\\mathbb{P}_{'+true_env+'}$')\n",
-    "    plt.xlabel('$\\mathtt{TP}_{'+true_env+'}$')\n",
+    "    plt.legend(prop={\"size\": 10})\n",
+    "\n",
+    "    plt.title(\"Lower bound of $\\mathbb{P}_{\" + true_env + \"}$ as a function of $\\mathtt{TP}_{\" + true_env + \"}$.\")\n",
+    "    plt.ylabel(\"$\\mathbb{P}_{\" + true_env + \"}$\")\n",
+    "    plt.xlabel(\"$\\mathtt{TP}_{\" + true_env + \"}$\")\n",
+    "\n",
     "\n",
     "plot_probabilities_bounds(points_ped, tpped_vals, [], lbounds_ped, \"ped\")\n",
     "plot_probabilities_bounds(points_obj, tpobj_vals, [], lbounds_obj, \"obj\")\n",
@@ -230,74 +227,56 @@
     "## Control contract for pedestrian class\n",
     "def control_contract_ped(lbounds_ped):\n",
     "    lb_m, lb_c = lbounds_ped\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_ped\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_ped\"],\n",
+    "        \"output_vars\": [\"P_ped\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_ped\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"tp_ped\": -1}, \"constant\": -0.6},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_ped\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"tp_ped\":-1},\n",
-    "            \"constant\":-0.6}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"tp_ped\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P_ped\": -1, \"tp_ped\": lb_m}, \"constant\": -1 * lb_c}],\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_obj(lbounds_obj):\n",
     "    lb_m, lb_c = lbounds_obj\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_obj\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_obj\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_obj\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"tp_obj\":-1},\n",
-    "            \"constant\":-0.3}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_obj\"],\n",
+    "        \"output_vars\": [\"P_obj\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_obj\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"tp_obj\": -1}, \"constant\": -0.3},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_obj\":-1, \"tp_obj\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P_obj\": -1, \"tp_obj\": lb_m}, \"constant\": -1 * lb_c}],\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "def control_contract_emp(lbounds_emp):\n",
     "    lb_m, lb_c = lbounds_emp\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"tp_emp\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_emp\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"tp_emp\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"tp_emp\":-1},\n",
-    "            \"constant\":-0.6}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"tp_emp\"],\n",
+    "        \"output_vars\": [\"P_emp\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"tp_emp\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"tp_emp\": -1}, \"constant\": -0.6},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_emp\":-1, \"tp_emp\":lb_m},\n",
-    "          \"constant\":-1*lb_c}\n",
-    "        ]}\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P_emp\": -1, \"tp_emp\": lb_m}, \"constant\": -1 * lb_c}],\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cped = PolyhedralContract.from_dict(control_contract_ped(lbounds_ped))\n",
     "Cobj = PolyhedralContract.from_dict(control_contract_obj(lbounds_obj))\n",
     "Cemp = PolyhedralContract.from_dict(control_contract_emp(lbounds_emp))\n",
     "\n",
     "print(\"Controller Contract for pedestrian class:\\n\" + str(Cped) + \"\\n\")\n",
-    "print(\"Controller Contract for object class:\\n\" + str(Cobj)+ \"\\n\")\n",
-    "print(\"Controller Contract for empty class:\\n\" + str(Cemp)+ \"\\n\")"
+    "print(\"Controller Contract for object class:\\n\" + str(Cobj) + \"\\n\")\n",
+    "print(\"Controller Contract for empty class:\\n\" + str(Cemp) + \"\\n\")"
    ]
   },
   {
@@ -332,7 +311,7 @@
    ],
    "source": [
     "# Construct merger of contracts:\n",
-    "C_controller = Cped.merge(Cobj) \n",
+    "C_controller = Cped.merge(Cobj)\n",
     "C_controller = C_controller.merge(Cemp)\n",
     "\n",
     "print(\"Merged Controller Contract for all object classes:\\n\" + str(C_controller) + \"\\n\")"
@@ -368,28 +347,18 @@
     "## System contract: P >= 0.99\n",
     "def system_contract_dist():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"d\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_obj\", \"P_emp\"\n",
+    "        \"input_vars\": [\"d\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_obj\", \"P_emp\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"d\": -1}, \"constant\": 0}, {\"coefficients\": {\"d\": 1}, \"constant\": 10}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"d\": -0.099}, \"constant\": -0.99},\n",
+    "            {\"coefficients\": {\"P_obj\": -1, \"d\": -0.08}, \"constant\": -0.8},\n",
+    "            {\"coefficients\": {\"P_emp\": -1, \"d\": -0.095}, \"constant\": -0.95},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"d\":-1}, \"constant\":0},\n",
-    "            {\"coefficients\":{\"d\":1}, \"constant\":10}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1, \"d\":-0.099},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P_obj\":-1, \"d\":-0.08},\n",
-    "            \"constant\":-0.8},\n",
-    "            {\"coefficients\":{\"P_emp\":-1, \"d\":-0.095},\n",
-    "            \"constant\":-0.95},\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "Csys_dist = PolyhedralContract.from_dict(system_contract_dist())\n",
     "print(\"System Contract (distance bounds):\\n\" + str(Csys_dist) + \"\\n\")"
    ]
@@ -427,7 +396,7 @@
    "source": [
     "# Computing quotient contract:\n",
     "C_dist = Csys_dist.quotient(C_controller)\n",
-    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist)+\"\\n\")"
+    "print(\"Quotient Contract with distance bounds on true positive rates:\\n\" + str(C_dist) + \"\\n\")"
    ]
   },
   {
diff --git a/case_studies/evaluating_perception/simple_evaluating_perception.ipynb b/case_studies/evaluating_perception/simple_evaluating_perception.ipynb
index 78a52c39..725bf92c 100644
--- a/case_studies/evaluating_perception/simple_evaluating_perception.ipynb
+++ b/case_studies/evaluating_perception/simple_evaluating_perception.ipynb
@@ -15,15 +15,14 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from gear.iocontract.utils import getVarlist\n",
-    "from PIL import Image\n",
-    "from gear.terms.polyhedra.loaders import readContract, writeContract\n",
-    "import matplotlib.pyplot as plt\n",
     "import os\n",
+    "\n",
+    "import matplotlib.pyplot as plt\n",
     "import numpy as np\n",
+    "from gear.terms.polyhedra.loaders import readContract\n",
     "from matplotlib import patches\n",
-    "import pdb\n",
-    "from matplotlib.collections import PatchCollection"
+    "from matplotlib.collections import PatchCollection\n",
+    "from PIL import Image"
    ]
   },
   {
@@ -69,60 +68,38 @@
     "## System contract: P >= 0.99\n",
     "def system_contract():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"x_e\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"x_e\":-1}, \"constant\":-1},\n",
-    "            {\"coefficients\":{\"x_e\":1}, \"constant\":1}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P\":-1},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P\":1},\n",
-    "            \"constant\":1}\n",
-    "        ]}\n",
+    "        \"input_vars\": [\"x_e\"],\n",
+    "        \"output_vars\": [\"P\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"x_e\": -1}, \"constant\": -1}, {\"coefficients\": {\"x_e\": 1}, \"constant\": 1}],\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P\": -1}, \"constant\": -0.99}, {\"coefficients\": {\"P\": 1}, \"constant\": 1}],\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "## Perception contract P = p**5 = 5p - 4\n",
     "# Control contract for pedestrian\n",
     "def control_contract():\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"p\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"p\"],\n",
+    "        \"output_vars\": [\"P\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"p\": 1}, \"constant\": 1}, {\"coefficients\": {\"p\": -1}, \"constant\": 0}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P\": 1, \"p\": -5}, \"constant\": -4},\n",
+    "            {\"coefficients\": {\"P\": -1, \"p\": 5}, \"constant\": 4},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"p\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"p\":-1},\n",
-    "            \"constant\":0}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P\":1, \"p\":-5},\n",
-    "          \"constant\":-4},\n",
-    "          {\"coefficients\":{\"P\":-1, \"p\":5},\n",
-    "          \"constant\":4}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
     "\n",
-    "\n",
     "Cs = readContract(system_contract())\n",
     "Ck = readContract(control_contract())\n",
     "\n",
-    "print(\"Overall System Contract:\\n\" + str(Cs)+\"\\n\")\n",
-    "print(\"Controller Contract:\\n\" + str(Ck)+\"\\n\")\n",
+    "print(\"Overall System Contract:\\n\" + str(Cs) + \"\\n\")\n",
+    "print(\"Controller Contract:\\n\" + str(Ck) + \"\\n\")\n",
     "\n",
     "Cp = Cs.quotient(Ck)\n",
-    "print(\"Quotient Contract:\\n\" + str(Cp)+\"\\n\")"
+    "print(\"Quotient Contract:\\n\" + str(Cp) + \"\\n\")"
    ]
   },
   {
@@ -141,15 +118,15 @@
    ],
    "source": [
     "TILESIZE = 50\n",
-    "ORIENTATIONS = {'n': 270, 'e': 0, 's': 90,'w':180, 'ne':315, 'nw':225, 'se':45, 'sw':135}\n",
+    "ORIENTATIONS = {\"n\": 270, \"e\": 0, \"s\": 90, \"w\": 180, \"ne\": 315, \"nw\": 225, \"se\": 45, \"sw\": 135}\n",
     "START_CROSSWALK = -1\n",
     "END_CROSSWALK = 2\n",
     "CROSSWALK_V = 1\n",
     "CROSSWALK_LOCATIONS = dict()\n",
     "ped_loc = []\n",
-    "for i, num in enumerate(range(2*START_CROSSWALK,2*(END_CROSSWALK+1))):\n",
-    "    CROSSWALK_LOCATIONS.update({i: (num/2, CROSSWALK_V)})\n",
-    "    ped_loc.append(num/2)\n",
+    "for i, num in enumerate(range(2 * START_CROSSWALK, 2 * (END_CROSSWALK + 1))):\n",
+    "    CROSSWALK_LOCATIONS.update({i: (num / 2, CROSSWALK_V)})\n",
+    "    ped_loc.append(num / 2)\n",
     "print(CROSSWALK_LOCATIONS)"
    ]
   },
@@ -188,23 +165,23 @@
     }
    ],
    "source": [
-    "class Grid():\n",
+    "class Grid:\n",
     "    def __init__(self):\n",
     "        self.xc_init = 0\n",
     "        self.vc_init = 0\n",
     "        self.ped = 1\n",
     "        self.N = 10\n",
     "        self.Vmax = 2\n",
-    "        self.xped = 10-CROSSWALK_V\n",
+    "        self.xped = 10 - CROSSWALK_V\n",
     "        self.main_dir = os.path.dirname(os.path.dirname(os.path.realpath(\"__file__\")))\n",
-    "        self.car_fig = self.main_dir + '/evaluating_perception/imglib/red_car.png'\n",
-    "        self.ped_fig = self.main_dir + '/evaluating_perception/imglib/pedestrian_img.png'\n",
+    "        self.car_fig = self.main_dir + \"/evaluating_perception/imglib/red_car.png\"\n",
+    "        self.ped_fig = self.main_dir + \"/evaluating_perception/imglib/pedestrian_img.png\"\n",
     "        self.car_data = [9, 0]\n",
     "        if self.ped == 1:\n",
     "            self.ped_data = [CROSSWALK_V, min(ped_loc)]\n",
     "        else:\n",
     "            self.ped_data = None\n",
-    "    \n",
+    "\n",
     "    def dynamics(self):\n",
     "        transitions = dict()\n",
     "        for x in range(0, self.N):\n",
@@ -213,7 +190,7 @@
     "                x_succ.append(x + v)\n",
     "            transitions[x] = x_succ\n",
     "        return transitions\n",
-    "    \n",
+    "\n",
     "    def plot_grid(self):\n",
     "        size = [1, self.N]\n",
     "        x_min = 0\n",
@@ -221,9 +198,9 @@
     "        y_min = 0\n",
     "        y_max = size[1] * TILESIZE\n",
     "        # x_min, x_max, y_min, y_max = get_map_corners(map)\n",
-    "        \n",
+    "\n",
     "        fig, ax = plt.subplots()\n",
-    "        ax.axis('equal')\n",
+    "        ax.axis(\"equal\")\n",
     "        ax.set_xlim(x_min, x_max)\n",
     "        ax.set_ylim(y_min, y_max)\n",
     "        ax.xaxis.set_visible(False)\n",
@@ -231,12 +208,14 @@
     "\n",
     "        # fill in the road regions\n",
     "        road_tiles = []\n",
-    "        width_tiles = np.arange(0,size[0]+1)*2*TILESIZE\n",
-    "        lanes_tiles = np.arange(0,size[1]+1)*TILESIZE\n",
-    "        \n",
-    "        for i in np.arange(0,size[0]):\n",
-    "            for k in np.arange(0,size[1]+1):\n",
-    "                tile = patches.Rectangle((width_tiles[i],lanes_tiles[k]),2*TILESIZE,TILESIZE,linewidth=1,facecolor='k', alpha=0.4)\n",
+    "        width_tiles = np.arange(0, size[0] + 1) * 2 * TILESIZE\n",
+    "        lanes_tiles = np.arange(0, size[1] + 1) * TILESIZE\n",
+    "\n",
+    "        for i in np.arange(0, size[0]):\n",
+    "            for k in np.arange(0, size[1] + 1):\n",
+    "                tile = patches.Rectangle(\n",
+    "                    (width_tiles[i], lanes_tiles[k]), 2 * TILESIZE, TILESIZE, linewidth=1, facecolor=\"k\", alpha=0.4\n",
+    "                )\n",
     "                road_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(road_tiles, match_original=True))\n",
     "\n",
@@ -244,41 +223,45 @@
     "        crosswalk_tiles = []\n",
     "        for item in CROSSWALK_LOCATIONS.keys():\n",
     "            if item % 2 == 0:\n",
-    "                color = 'silver'\n",
+    "                color = \"silver\"\n",
     "                alpha = 0.5\n",
     "            else:\n",
-    "                color = 'k'\n",
+    "                color = \"k\"\n",
     "                alpha = 0.5\n",
-    "            width = CROSSWALK_LOCATIONS[item][0]*TILESIZE\n",
-    "            lanes = CROSSWALK_LOCATIONS[item][1]*TILESIZE\n",
-    "            tile = patches.Rectangle((width,lanes),TILESIZE/2,TILESIZE,linewidth=1,facecolor=color, alpha=alpha)\n",
+    "            width = CROSSWALK_LOCATIONS[item][0] * TILESIZE\n",
+    "            lanes = CROSSWALK_LOCATIONS[item][1] * TILESIZE\n",
+    "            tile = patches.Rectangle((width, lanes), TILESIZE / 2, TILESIZE, linewidth=1, facecolor=color, alpha=alpha)\n",
     "            crosswalk_tiles.append(tile)\n",
     "        ax.add_collection(PatchCollection(crosswalk_tiles, match_original=True))\n",
     "        plt.gca().invert_yaxis()\n",
     "        return ax\n",
-    "    \n",
-    "    \n",
+    "\n",
     "    def plot_car(self, ax):\n",
     "        y_tile, speed = self.car_data\n",
-    "        theta_d = 270 # Rotate car to face north\n",
-    "        x = (1) * TILESIZE/2\n",
+    "        theta_d = 270  # Rotate car to face north\n",
+    "        x = (1) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        car_fig = Image.open(self.car_fig)\n",
     "        car_fig = car_fig.rotate(theta_d, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(car_fig, zorder=1, interpolation='bilinear', extent=[x+2, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            car_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 2, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
-    "    \n",
+    "\n",
     "    def plot_ped(self, ax):\n",
     "        y_tile, x_tile = self.ped_data\n",
-    "        x = (x_tile) * TILESIZE/2\n",
+    "        x = (x_tile) * TILESIZE / 2\n",
     "        y = (y_tile) * TILESIZE\n",
     "        ped_fig = Image.open(self.ped_fig)\n",
     "        ped_fig = ped_fig.rotate(180, expand=False)\n",
     "        offset = 0.1\n",
-    "        ax.imshow(ped_fig, zorder=1, interpolation='bilinear', extent=[x+4, x+TILESIZE-2, y+2, y+TILESIZE-2])\n",
+    "        ax.imshow(\n",
+    "            ped_fig, zorder=1, interpolation=\"bilinear\", extent=[x + 4, x + TILESIZE - 2, y + 2, y + TILESIZE - 2]\n",
+    "        )\n",
     "        return ax\n",
     "\n",
+    "\n",
     "# Generate grid and figure:\n",
     "grid = Grid()\n",
     "ax = grid.plot_grid()\n",
@@ -359,101 +342,77 @@
     "## System contract: P >= 0.99\n",
     "def system_contract():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "             \"x_e\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\", \"P_notped\"\n",
+    "        \"input_vars\": [\"x_e\"],\n",
+    "        \"output_vars\": [\"P_ped\", \"P_notped\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"x_e\": -1}, \"constant\": -1}, {\"coefficients\": {\"x_e\": 1}, \"constant\": 1}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": -1}, \"constant\": -0.99},\n",
+    "            {\"coefficients\": {\"P_ped\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"P_notped\": -1}, \"constant\": -0.90},\n",
+    "            {\"coefficients\": {\"P_notped\": 1}, \"constant\": 1},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "             {\"coefficients\":{\"x_e\":-1}, \"constant\":-1},\n",
-    "             {\"coefficients\":{\"x_e\":1}, \"constant\":1}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P_ped\":-1},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P_ped\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"P_notped\":-1},\n",
-    "            \"constant\":-0.90},\n",
-    "            {\"coefficients\":{\"P_notped\":1},\n",
-    "            \"constant\":1}\n",
-    "        ]}\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "## Perception contract P = p**5 = 5p - 4\n",
     "# Control contract for pedestrian\n",
     "def control_contract_ped():\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"p_ped\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_ped\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"p_ped\"],\n",
+    "        \"output_vars\": [\"P_ped\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"p_ped\": 1}, \"constant\": 1}, {\"coefficients\": {\"p_ped\": -1}, \"constant\": 0}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_ped\": 1, \"p_ped\": -5}, \"constant\": -4},\n",
+    "            {\"coefficients\": {\"P_ped\": -1, \"p_ped\": 5}, \"constant\": 4},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"p_ped\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"p_ped\":-1},\n",
-    "            \"constant\":0}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_ped\":1, \"p_ped\":-5},\n",
-    "          \"constant\":-4},\n",
-    "          {\"coefficients\":{\"P_ped\":-1, \"p_ped\":5},\n",
-    "          \"constant\":4}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "## Perception contract P = p**5 = 5(1-p) - 4\n",
     "# Control contract for pedestrian\n",
     "def control_contract_not_ped():\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"p_notped\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P_notped\"\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"p_notped\"],\n",
+    "        \"output_vars\": [\"P_notped\"],\n",
+    "        \"assumptions\": [\n",
+    "            {\"coefficients\": {\"p_notped\": 1}, \"constant\": 1},\n",
+    "            {\"coefficients\": {\"p_notped\": -1}, \"constant\": 0},\n",
     "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"p_notped\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"p_notped\":-1},\n",
-    "            \"constant\":0}\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P_notped\": 1, \"p_notped\": -4.2}, \"constant\": -3.2},\n",
+    "            {\"coefficients\": {\"P_notped\": -1, \"p_notped\": 4.2}, \"constant\": 3.2},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P_notped\":1, \"p_notped\":-4.2},\n",
-    "          \"constant\":-3.2},\n",
-    "          {\"coefficients\":{\"P_notped\":-1, \"p_notped\":4.2},\n",
-    "          \"constant\":3.2}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
+    "\n",
     "Cs = readContract(system_contract())\n",
     "Ck_ped = readContract(control_contract_ped())\n",
     "Ck_notped = readContract(control_contract_not_ped())\n",
     "\n",
     "\n",
-    "print(\"Overall System Contract:\\n\" + str(Cs)+\"\\n\")\n",
-    "print(\"Controller Contract (true env: ped):\\n\" + str(Ck_ped)+\"\\n\")\n",
-    "print(\"Controller Contract (true env:not ped):\\n\" + str(Ck_ped)+\"\\n\")\n",
+    "print(\"Overall System Contract:\\n\" + str(Cs) + \"\\n\")\n",
+    "print(\"Controller Contract (true env: ped):\\n\" + str(Ck_ped) + \"\\n\")\n",
+    "print(\"Controller Contract (true env:not ped):\\n\" + str(Ck_ped) + \"\\n\")\n",
     "\n",
     "# Quotient for ped controller\n",
     "Cpped = Cs.quotient(Ck_ped)\n",
-    "print(\"Quotient Contract:\\n\" + str(Cpped)+\"\\n\")\n",
+    "print(\"Quotient Contract:\\n\" + str(Cpped) + \"\\n\")\n",
     "\n",
     "# Quotient for not ped controller:\n",
     "Cp = Cs.quotient(Ck_notped)\n",
-    "print(\"Quotient Contract:\\n\" + str(Cp)+\"\\n\")\n",
+    "print(\"Quotient Contract:\\n\" + str(Cp) + \"\\n\")\n",
     "\n",
     "# Composed contract:\n",
     "Ck_comp = Ck_ped.compose(Ck_notped)\n",
-    "print(\"Composed Control Contract:\\n\" + str(Ck_comp)+\"\\n\")\n",
+    "print(\"Composed Control Contract:\\n\" + str(Ck_comp) + \"\\n\")\n",
     "\n",
     "Cp = Cs.quotient(Ck_comp)\n",
-    "print(\"Quotient Contract for composed control contract:\\n\" + str(Cp)+\"\\n\")"
+    "print(\"Quotient Contract for composed control contract:\\n\" + str(Cp) + \"\\n\")"
    ]
   },
   {
@@ -464,59 +423,62 @@
    "outputs": [],
    "source": [
     "# Need to specify Markov Chain as a symbolic library\n",
-    "class MC():\n",
+    "class MC:\n",
     "    def __init__(self, states, transitions, goal):\n",
     "        self.S = states\n",
-    "        self. T = transitions\n",
+    "        self.T = transitions\n",
     "        self.F = goal\n",
-    "        self.P = {t: 0 for t in self.T} # Initialize transition probabilities\n",
-    "        \n",
+    "        self.P = {t: 0 for t in self.T}  # Initialize transition probabilities\n",
+    "\n",
     "    def read_probabilities(self, transitions, P):\n",
     "        pass\n",
     "\n",
-    "class CM():\n",
+    "\n",
+    "class CM:\n",
     "    def __init__(self, classes):\n",
     "        self.classes = classes\n",
     "        self.row_data_type = \"predicted_class\"\n",
     "        self.col_data_type = \"true_class\"\n",
     "        self.class_dict = {i: classes[i] for i in range(len(classes))}\n",
-    "        self.rev_class_dict = {v: k for k,v in self.class_dict.items()}\n",
+    "        self.rev_class_dict = {v: k for k, v in self.class_dict.items()}\n",
     "        self.cm = np.zeros((len(classes)))\n",
-    "    \n",
+    "\n",
     "    def set_cm(self, cm):\n",
     "        self.cm = cm\n",
-    "    \n",
+    "\n",
     "    def get_tpr(self, class_i):\n",
     "        class_ind = self.rev_class_dict[class_i]\n",
     "        return self.cm[class_ind, class_ind]\n",
-    "    \n",
+    "\n",
     "    def get_fpr(self, class_i):\n",
     "        class_ind = self.rev_class_dict[class_i]\n",
     "        fpr = sum(self.cm[class_ind, :]) - self.cm[class_ind, class_ind]\n",
     "        return fpr\n",
-    "    \n",
+    "\n",
     "    def get_fnr(self, class_i):\n",
     "        class_ind = self.rev_class_dict[class_i]\n",
     "        total_samples = self.get_total_samples(class_i)\n",
     "        return total_samples - self.cm[class_ind, class_ind]\n",
-    "    \n",
+    "\n",
     "    def get_total_samples(self, class_i):\n",
     "        class_ind = self.rev_class_dict[class_i]\n",
     "        total_samples = sum(self.cm[:, class_ind])\n",
-    "        return total_samples \n",
-    "    \n",
+    "        return total_samples\n",
+    "\n",
     "    def get_normalized_metrics(self, class_i):\n",
     "        total_samples = self.get_total_samples(class_i)\n",
     "        tpr = self.get_tpr(class_i)\n",
     "        fpr = self.get_fpr(class_i)\n",
     "        fnr = self.get_fnr(class_i)\n",
-    "        return tpr/total_samples, fpr/total_samples, fnr/total_samples\n",
-    "    \n",
+    "        return tpr / total_samples, fpr / total_samples, fnr / total_samples\n",
+    "\n",
+    "\n",
     "# Define child classes\n",
-    "class Proposition_CM():\n",
+    "class Proposition_CM:\n",
     "    pass\n",
     "\n",
-    "class Distance_CM():\n",
+    "\n",
+    "class Distance_CM:\n",
     "    pass"
    ]
   },
@@ -529,7 +491,7 @@
    "source": [
     "## Object detection contract:\n",
     "# Inputs: classes\n",
-    "# Outputs: true positive and false positive rates for each class\n"
+    "# Outputs: true positive and false positive rates for each class"
    ]
   }
  ],
diff --git a/case_studies/evaluating_perception/simple_script.ipynb b/case_studies/evaluating_perception/simple_script.ipynb
index 59d57a0e..36c66ef4 100644
--- a/case_studies/evaluating_perception/simple_script.ipynb
+++ b/case_studies/evaluating_perception/simple_script.ipynb
@@ -28,14 +28,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Import libraries:\n",
-    "import pacti.iocontract as iocontract\n",
-    "from pacti.iocontract.utils import getVarlist\n",
-    "from pacti.terms.polyhedra.loaders import readContract, writeContract\n",
-    "from IPython.display import display\n",
-    "from IPython.display import Latex\n",
+    "import matplotlib.image as mpimg\n",
     "import matplotlib.pyplot as plt\n",
-    "import matplotlib.image as mpimg"
+    "\n",
+    "# Import libraries:\n",
+    "from pacti.terms.polyhedra.loaders import readContract"
    ]
   },
   {
@@ -63,7 +60,7 @@
    "source": [
     "%matplotlib inline\n",
     "fig, ax = plt.subplots()\n",
-    "img = mpimg.imread('imglib/simple_case_study_fig2.png')\n",
+    "img = mpimg.imread(\"imglib/simple_case_study_fig2.png\")\n",
     "imgplot = ax.imshow(img)\n",
     "ax.xaxis.set_visible(False)\n",
     "ax.yaxis.set_visible(False)\n",
@@ -115,54 +112,27 @@
     "## System contract: P >= 0.99\n",
     "def system_contract():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"x_e\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"x_e\":-1}, \"constant\":-1},\n",
-    "            {\"coefficients\":{\"x_e\":1}, \"constant\":1}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P\":-1},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P\":1},\n",
-    "            \"constant\":1}\n",
-    "        ]}\n",
+    "        \"input_vars\": [\"x_e\"],\n",
+    "        \"output_vars\": [\"P\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"x_e\": -1}, \"constant\": -1}, {\"coefficients\": {\"x_e\": 1}, \"constant\": 1}],\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P\": -1}, \"constant\": -0.99}, {\"coefficients\": {\"P\": 1}, \"constant\": 1}],\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "## Perception contract P = p**5 = 5p - 4\n",
     "def control_contract():\n",
-    "    mc= dict()\n",
+    "    mc = dict()\n",
     "    mc = {\n",
-    "        \"input_vars\":[\n",
-    "            \"p\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"p\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"p\":-1},\n",
-    "            \"constant\":0}\n",
+    "        \"input_vars\": [\"p\"],\n",
+    "        \"output_vars\": [\"P\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"p\": 1}, \"constant\": 1}, {\"coefficients\": {\"p\": -1}, \"constant\": 0}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P\": 1, \"p\": -5}, \"constant\": -4},\n",
+    "            {\"coefficients\": {\"P\": -1, \"p\": 5}, \"constant\": 4},\n",
+    "            {\"coefficients\": {\"P\": -1}, \"constant\": -0.99},\n",
+    "            {\"coefficients\": {\"P\": 1}, \"constant\": 1},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P\":1, \"p\":-5},\n",
-    "          \"constant\":-4},\n",
-    "          {\"coefficients\":{\"P\":-1, \"p\":5},\n",
-    "          \"constant\":4},\n",
-    "          {\"coefficients\":{\"P\":-1},\n",
-    "          \"constant\":-0.99},\n",
-    "          {\"coefficients\":{\"P\":1},\n",
-    "          \"constant\":1}\n",
-    "        ]\n",
     "    }\n",
     "    return mc\n",
     "\n",
@@ -170,7 +140,7 @@
     "Cs = readContract(system_contract())\n",
     "Ck = readContract(control_contract())\n",
     "\n",
-    "print(\"Overall System Contract:\\n\" + str(Cs)+\"\\n\")\n",
+    "print(\"Overall System Contract:\\n\" + str(Cs) + \"\\n\")\n",
     "print(\"Controller Contract:\\n\" + str(Ck))"
    ]
   },
@@ -260,55 +230,33 @@
     "## System contract: P >= 0.99\n",
     "def system_contract():\n",
     "    system_contract = {\n",
-    "        \"input_vars\":[\n",
-    "            \"x_e\"\n",
-    "        ],\n",
-    "        \"output_vars\":[\n",
-    "            \"P\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"x_e\":-1}, \"constant\":-1},\n",
-    "            {\"coefficients\":{\"x_e\":1}, \"constant\":1}\n",
-    "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"P\":-1},\n",
-    "            \"constant\":-0.99},\n",
-    "            {\"coefficients\":{\"P\":1},\n",
-    "            \"constant\":1}\n",
-    "        ]}\n",
+    "        \"input_vars\": [\"x_e\"],\n",
+    "        \"output_vars\": [\"P\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"x_e\": -1}, \"constant\": -1}, {\"coefficients\": {\"x_e\": 1}, \"constant\": 1}],\n",
+    "        \"guarantees\": [{\"coefficients\": {\"P\": -1}, \"constant\": -0.99}, {\"coefficients\": {\"P\": 1}, \"constant\": 1}],\n",
+    "    }\n",
     "    return system_contract\n",
     "\n",
+    "\n",
     "## Perception contract P = p**5 = 5p - 4\n",
     "def control_contract():\n",
-    "    mc= dict()\n",
-    "    mc = {\"input_vars\":[\"p\"],\n",
-    "        \"output_vars\":[\n",
-    "            \"P\"\n",
-    "        ],\n",
-    "        \"assumptions\":\n",
-    "        [\n",
-    "            {\"coefficients\":{\"p\":1},\n",
-    "            \"constant\":1},\n",
-    "            {\"coefficients\":{\"p\":-1},\n",
-    "            \"constant\":0}\n",
+    "    mc = dict()\n",
+    "    mc = {\n",
+    "        \"input_vars\": [\"p\"],\n",
+    "        \"output_vars\": [\"P\"],\n",
+    "        \"assumptions\": [{\"coefficients\": {\"p\": 1}, \"constant\": 1}, {\"coefficients\": {\"p\": -1}, \"constant\": 0}],\n",
+    "        \"guarantees\": [\n",
+    "            {\"coefficients\": {\"P\": 1, \"p\": -5}, \"constant\": -4},\n",
+    "            {\"coefficients\": {\"P\": -1, \"p\": 5}, \"constant\": 4},\n",
     "        ],\n",
-    "        \"guarantees\":\n",
-    "        [\n",
-    "          {\"coefficients\":{\"P\":1, \"p\":-5},\n",
-    "          \"constant\":-4},\n",
-    "          {\"coefficients\":{\"P\":-1, \"p\":5},\n",
-    "          \"constant\":4}\n",
-    "        ]}\n",
+    "    }\n",
     "    return mc\n",
     "\n",
     "\n",
-    "\n",
     "Cs = readContract(system_contract())\n",
     "Ck = readContract(control_contract())\n",
     "\n",
-    "print(\"Overall System Contract:\\n\" + str(Cs)+\"\\n\")\n",
+    "print(\"Overall System Contract:\\n\" + str(Cs) + \"\\n\")\n",
     "print(\"Controller Contract:\\n\" + str(Ck))"
    ]
   },
@@ -342,7 +290,7 @@
    ],
    "source": [
     "Cp = Cs.quotient(Ck)\n",
-    "print(\"Quotient Contract:\\n\" + str(Cp)+\"\\n\")"
+    "print(\"Quotient Contract:\\n\" + str(Cp) + \"\\n\")"
    ]
   },
   {
diff --git a/case_studies/evaluating_perception/utils.py b/case_studies/evaluating_perception/utils.py
index de3c2b1e..18eed279 100644
--- a/case_studies/evaluating_perception/utils.py
+++ b/case_studies/evaluating_perception/utils.py
@@ -1,24 +1,18 @@
-import matplotlib.pyplot as plt
 import os
-import numpy as np
-from matplotlib import patches
 import pdb
 import sys
-sys.path.append('/Users/apurvabadithela/Documents/software/tulip-control/')
-from matplotlib.collections import PatchCollection
+
+sys.path.append("/Users/apurvabadithela/Documents/software/tulip-control/")
+from itertools import product
+
+from tulip.interfaces import stormpy as stormpy_int
 from tulip.transys import MarkovChain as MC
 from tulip.transys import MarkovDecisionProcess as MDP
-from itertools import compress, product
-from tulip.interfaces import stormpy as stormpy_int
 from tulip.transys.compositions import synchronous_parallel
 
 # Importing libraries to setup markov chain
 # Setting up the base controllers:
-import design_controller4 as K_des
-import ped_controller as Kped
-import not_ped_controller as Kobj
-import empty_controller as Kempty
-import importlib
+
 
 # Function that converts to a Markov chain from states and actions:
 def _construct_mdpmc(states, transitions, init, actions=None):
@@ -45,6 +39,7 @@ def _construct_mdpmc(states, transitions, init, actions=None):
         ts.states[s]["ap"] = {s}
     return ts
 
+
 # Function to return a list of all combinations of inputs:
 # Input: A dictionary names D
 # Each key of D corresponds to a set of values that key can take
@@ -55,35 +50,42 @@ def dict_combinations(D):
     prod_input = list(product(*values))
     return prod_input
 
+
 ## Parametric Markov chain synthesis class:
 model_MC = "model_MC.nm"
+
+
 class MarkovChain:
     def __init__(self, S, O, state_to_S):
-        self.states = S    # Product states for car.
+        self.states = S  # Product states for car.
         self.state_dict = state_to_S
         self.reverse_state_dict = {v: k for k, v in state_to_S.items()}
         self.obs = O
-        self.true_env = None # This state is defined in terms of the observation
-        self.true_env_type = None # Type of the env object; is in one of obs
-        self.C = dict() # Confusion matrix dictionary giving: C[obs, true] =  P(obs |- phi | true |- phi)
-        self.M = dict() # Two-by-two dictionary. a(i,j) = Prob of transitioning from state i to state j
-        self.K = None # Depending on the observation, the controller changes. This is a dictionary of the controller after the Mealy machine is syntehsized from specifications
-        self.K_strategy = None # Dictionary containing the scripts to the controller after it has been written to file
+        self.true_env = None  # This state is defined in terms of the observation
+        self.true_env_type = None  # Type of the env object; is in one of obs
+        self.C = dict()  # Confusion matrix dictionary giving: C[obs, true] =  P(obs |- phi | true |- phi)
+        self.M = dict()  # Two-by-two dictionary. a(i,j) = Prob of transitioning from state i to state j
+        self.K = None  # Depending on the observation, the controller changes. This is a dictionary of the controller after the Mealy machine is syntehsized from specifications
+        self.K_strategy = None  # Dictionary containing the scripts to the controller after it has been written to file
         self.formula = []
-        self.K_int_state_map = dict() # Nested dictionary mapping internal states to concrete states of the controller instantiation
-        self.K_int_state_map_inv = dict() # Nested dictionary mapping concrete states of controller instance into internal states
-        self.MC = None # A Tulip Markov chain object that is consistent with TuLiP transition system markov chain
-        self.true_env_MC = None # A Markov chain representing the true evolution of the environment
-        self.backup = dict() # This is a backup controller.
+        self.K_int_state_map = (
+            dict()
+        )  # Nested dictionary mapping internal states to concrete states of the controller instantiation
+        self.K_int_state_map_inv = (
+            dict()
+        )  # Nested dictionary mapping concrete states of controller instance into internal states
+        self.MC = None  # A Tulip Markov chain object that is consistent with TuLiP transition system markov chain
+        self.true_env_MC = None  # A Markov chain representing the true evolution of the environment
+        self.backup = dict()  # This is a backup controller.
 
     # Convert this Markov chain object into a tulip transition system:
     def to_MC(self, init):
-        states = set(self.states) # Set of product states of the car
+        states = set(self.states)  # Set of product states of the car
         transitions = set()
         for k in self.M.keys():
-            p_approx = min(1, abs(self.M[k])) # Probabilities cannot be greater than 1
-            if abs(1-self.M[k]) > 1e-2:
-                print(abs(1-self.M[k]))
+            p_approx = min(1, abs(self.M[k]))  # Probabilities cannot be greater than 1
+            if abs(1 - self.M[k]) > 1e-2:
+                print(abs(1 - self.M[k]))
             t = (k[0], k[1], p_approx)
             transitions |= {t}
         assert init in self.states
@@ -91,7 +93,7 @@ def to_MC(self, init):
         markov_chain = _construct_mdpmc(states, transitions, init)
         for state in self.MC.states:
             self.MC.states[state]["ap"] = {state}
-        self.check_MC() # Checking if Markov chain is valid
+        self.check_MC()  # Checking if Markov chain is valid
         return markov_chain
 
     # Writing/Printing Markov chains to file:
@@ -104,26 +106,26 @@ def print_MC(self):
         stormpy_int.print_stormpy_model(path_MC_model)
         stormpy_int.print_stormpy_model(env_MC_model)
 
-   # Function to check if all outgoing transition probabilities for any state in the Markov chain sum to 1.
+    # Function to check if all outgoing transition probabilities for any state in the Markov chain sum to 1.
     def check_MC(self):
         T = self.MC.transitions(data=True)
         # Printing all states and outgoing transitions
         for st in self.MC.states:
             print("State: ", st)
-            end_states = [t for t in T if t[0]==st]
-            prob = [(t[2])['probability'] for t in end_states]
+            end_states = [t for t in T if t[0] == st]
+            prob = [(t[2])["probability"] for t in end_states]
             print("probability of end states: ", str(prob))
-            if not abs(sum(prob)-1)<1e-4:
+            if not abs(sum(prob) - 1) < 1e-4:
                 pdb.set_trace()
-            assert abs(sum(prob)-1)<1e-4 # Checking that probabilities add up to 1 for every state
+            assert abs(sum(prob) - 1) < 1e-4  # Checking that probabilities add up to 1 for every state
 
-   # Sets the state of the true environment
-   # True env type is the string form of the object and st is the corresponding state
+    # Sets the state of the true environment
+    # True env type is the string form of the object and st is the corresponding state
     def set_true_env_state(self, st, true_env_type):
         self.true_env = st
-        new_st = "p"+st
+        new_st = "p" + st
         states = {new_st}
-        transitions ={(new_st, new_st, 1)}
+        transitions = {(new_st, new_st, 1)}
         init = new_st
         self.true_env_type = true_env_type
         self.true_env_MC = _construct_mdpmc(states, transitions, init)
@@ -140,14 +142,18 @@ def set_controller(self, K, K_strategy, K_backup):
 
     # Determining transition to next state based on the env observed in the current state
     # This part is critical and depends on the controller corresponding to the predicted env state
-    def compute_next_state(self,obs,env_st, init_st): # The next state computed using the observation from the current state
-        Ki = self.K[obs] # The controller object
-        Ki_strategy =self.K_strategy[obs]
-        K_instant, sinit_adj, flg = self.adjust_state(Ki, Ki_strategy,obs,init_st) # If flg = 1, use sinit_adj as given by backup controller
+    def compute_next_state(
+        self, obs, env_st, init_st
+    ):  # The next state computed using the observation from the current state
+        Ki = self.K[obs]  # The controller object
+        Ki_strategy = self.K_strategy[obs]
+        K_instant, sinit_adj, flg = self.adjust_state(
+            Ki, Ki_strategy, obs, init_st
+        )  # If flg = 1, use sinit_adj as given by backup controller
         if flg == 0:
-            next_st = K_instant.move(*env_st) # Might have to modify this when there are multiple observations
+            next_st = K_instant.move(*env_st)  # Might have to modify this when there are multiple observations
         else:
-            next_st = {'xcar': sinit_adj[0], 'vcar': sinit_adj[1]}
+            next_st = {"xcar": sinit_adj[0], "vcar": sinit_adj[1]}
         return next_st
 
     # Function to construct a map of internal states of various controllers:
@@ -155,12 +161,12 @@ def construct_internal_state_maps(self):
         for obs in self.obs:
             Ki = self.K[obs]
             Ki_strategy = self.K_strategy[obs]
-            inputs = Ki.inputs # Input dictionary mapping input variables to range of values
-            outputs = Ki.outputs # Output dictionary mapping output variable to range of values
+            inputs = Ki.inputs  # Input dictionary mapping input variables to range of values
+            outputs = Ki.outputs  # Output dictionary mapping output variable to range of values
             input_comb = dict_combinations(inputs)
             output_comb = dict_combinations(outputs)
-            K_inst = Ki_strategy.TulipStrategy() # Instantiation
-            N_int_states = K_inst.state # Total no. of internal states
+            K_inst = Ki_strategy.TulipStrategy()  # Instantiation
+            N_int_states = K_inst.state  # Total no. of internal states
             self.K_int_state_map[Ki] = dict()
             self.K_int_state_map_inv[Ki] = dict()
 
@@ -170,41 +176,42 @@ def construct_internal_state_maps(self):
                 self.K_int_state_map_inv[Ki][inp] = dict()
                 # Populating the dictionary:
                 for ii in range(N_int_states):
-                    self.K_int_state_map[Ki][inp][ii] = None # None object
+                    self.K_int_state_map[Ki][inp][ii] = None  # None object
 
             # Modifying the list:
-            for int_st in range(N_int_states+1):
+            for int_st in range(N_int_states + 1):
                 # print("Internal state: ", str(int_st))
                 for inp in input_comb:
                     K_inst.state = int_st
-                    try: # Adding the environment state
+                    try:  # Adding the environment state
                         out = K_inst.move(*inp)
                         state = K_inst.state
                         # print("State moved: ", str(state))
-                        if (state == 2):
+                        if state == 2:
                             flg = 1
                         if self.K_int_state_map[Ki][inp][state] is None:
-                            self.K_int_state_map[Ki][inp][state] = list(out.values()) # Updating state
+                            self.K_int_state_map[Ki][inp][state] = list(out.values())  # Updating state
                             assert self.K_int_state_map[Ki][inp][state] is not None
                     except Exception:
                         pass
 
             # Reversing the internal state map:
             for inp in input_comb:
-                for k,v in self.K_int_state_map[Ki][inp].items():
+                for k, v in self.K_int_state_map[Ki][inp].items():
                     if v is not None:
                         self.K_int_state_map_inv[Ki][inp][tuple(v)] = k
 
-
     # Adjust state of the system based on the controller that's being used so it doesn't throw an error:
     # Ki is the controller and Ki_strategy is the script that corresponds to the strategy
     # of Ki. One of the variables returned is also K_instant (the instantiation of the controller)
-    def adjust_state(self,Ki, Ki_strategy, obs,  sinit_st):
-        flg =0 # Default
+    def adjust_state(self, Ki, Ki_strategy, obs, sinit_st):
+        flg = 0  # Default
         inp_st = tuple(self.get_env_state(obs))
         if sinit_st not in self.K_int_state_map_inv[Ki][inp_st].keys():
             poss_st = self.backup[sinit_st]
-            test_list = [p for p in poss_st if (p[0]==sinit_st[0] and p[1]==1)]  # If sinit_st is at zero velocity, car should not remain stuck at 0 velocity.
+            test_list = [
+                p for p in poss_st if (p[0] == sinit_st[0] and p[1] == 1)
+            ]  # If sinit_st is at zero velocity, car should not remain stuck at 0 velocity.
             flg = 1
             K_instant = None
             if test_list:
@@ -212,7 +219,7 @@ def adjust_state(self,Ki, Ki_strategy, obs,  sinit_st):
             else:
                 init_st_adj = poss_st[0]
         else:
-            init_st_adj=(self.K_int_state_map_inv[Ki][inp_st])[sinit_st] # Finding the mapping from internal states
+            init_st_adj = (self.K_int_state_map_inv[Ki][inp_st])[sinit_st]  # Finding the mapping from internal states
             print("Adjusted state: ", init_st_adj)
             K_instant = Ki_strategy.TulipStrategy()
             K_instant.state = init_st_adj
@@ -220,13 +227,17 @@ def adjust_state(self,Ki, Ki_strategy, obs,  sinit_st):
 
     # Function to return the state of the environment given the observation:
     def get_env_state(self, obs):
-        env_st =[1] # For static environment, env state is the same. Should modify this function for reactive environments
+        env_st = [
+            1
+        ]  # For static environment, env state is the same. Should modify this function for reactive environments
         if obs == self.true_env_type:
             env_st = [int(self.true_env)]
         return env_st
 
     # Constructing the Markov chain
-    def construct_markov_chain(self): # Construct probabilities and transitions in the markov chain given the controller and confusion matrix
+    def construct_markov_chain(
+        self,
+    ):  # Construct probabilities and transitions in the markov chain given the controller and confusion matrix
         for Si in list(self.states):
             print("Finding initial states in the Markov chain: ")
             print(Si)
@@ -246,9 +257,9 @@ def construct_markov_chain(self): # Construct probabilities and transitions in t
                 if Sj not in out_states:
                     out_states.append(Sj)
             sum_probs = sum([self.M[Si, Sj] for Sj in out_states])
-            if sum_probs > 1.0+1e-4:
+            if sum_probs > 1.0 + 1e-4:
                 pdb.set_trace()
-            assert (sum_probs <= 1+1e-4)
+            assert sum_probs <= 1 + 1e-4
         return self.M
 
     # Adding formulae to list of temporal logic formulas:
@@ -271,7 +282,7 @@ def prob_TL(self, phi):
         # result = stormpy_int.model_checking(composed, phi, prism_file_path)
         # Returns a tulip transys:
         # MC_ts = stormpy_int.to_tulip_transys(path_MC)
-        result = stormpy_int.model_checking(self.MC, phi, prism_file_path) #
+        result = stormpy_int.model_checking(self.MC, phi, prism_file_path)  #
         return result
 
     # Function to append labels to a .nm file:
@@ -279,16 +290,16 @@ def add_labels(self, MAX_V):
         model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "models")
         path_MC = os.path.join(model_path, prop_model_MC)
         for vi in range(0, MAX_V):
-            var = "label \"v_"+str(vi)+"\"="
-            flg_var = 0 # Set after the first state is appended
+            var = 'label "v_' + str(vi) + '"='
+            flg_var = 0  # Set after the first state is appended
             for k, val in self.state_dict.items():
                 if k[1] == vi:
                     if flg_var == 0:
                         flg_var = 1
                         var = var + "(s = "
                     var = var + ""
-            with open(path_MC, 'a') as out:
-                out.write(var + '\n')
+            with open(path_MC, "a") as out:
+                out.write(var + "\n")
 
     # Function to pretty print Markov Chain:
     def pretty_print(self):
@@ -297,5 +308,5 @@ def pretty_print(self):
             S_out, S_in = edge
             xin, vin = self.reverse_state_dict[S_in]
             xout, vout = self.reverse_state_dict[S_out]
-            print(f'Out: ({xout},{vout}), In: ({xin},{vin}) ==> Prob: {prob:4f}')
-            #print("Out: (",xout,vout, "In: (%d, %d) ",xin,vin, "Prob: %d ",prob)
+            print(f"Out: ({xout},{vout}), In: ({xin},{vin}) ==> Prob: {prob:4f}")
+            # print("Out: (",xout,vout, "In: (%d, %d) ",xin,vin, "Prob: %d ",prob)
diff --git a/case_studies/multiagent_coordination/multiagent.ipynb b/case_studies/multiagent_coordination/multiagent.ipynb
index 8618e2c9..e3ebacd0 100644
--- a/case_studies/multiagent_coordination/multiagent.ipynb
+++ b/case_studies/multiagent_coordination/multiagent.ipynb
@@ -47,12 +47,12 @@
    "outputs": [],
    "source": [
     "%matplotlib inline\n",
-    "from pacti.terms.polyhedra import PolyhedralContract\n",
+    "import numpy as np\n",
+    "from utils.multiagent_utils import Robot, check_collision_quadrants, distance, get_dynamic_collision_contract, strategy\n",
+    "from utils.plotting_utils import animate, plot_grid_world, save_trace\n",
+    "\n",
     "from pacti.iocontract import Var\n",
-    "from utils.multiagent_utils import Coord, Robot, check_collision_quadrants, distance, \\\n",
-    "strategy, get_dynamic_collision_contract\n",
-    "from utils.plotting_utils import save_trace, plot_grid_world, animate\n",
-    "import numpy as np"
+    "from pacti.terms.polyhedra import PolyhedralContract"
    ]
   },
   {
@@ -94,12 +94,12 @@
     "n = 6\n",
     "m = 3\n",
     "\n",
-    "# Initialize robots        \n",
-    "r1 = Robot('A', (0,0), (5,2))\n",
-    "r2 = Robot('B', (0,2), (5,0))\n",
-    "robots = [r1,r2]\n",
+    "# Initialize robots\n",
+    "r1 = Robot(\"A\", (0, 0), (5, 2))\n",
+    "r2 = Robot(\"B\", (0, 2), (5, 0))\n",
+    "robots = [r1, r2]\n",
     "\n",
-    "plot_grid_world(n,m,robots)"
+    "plot_grid_world(n, m, robots)"
    ]
   },
   {
@@ -169,34 +169,34 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def get_dynamics_contract_robot(name, init_pos, timestep): \n",
-    "    X_str_0 = 'x_'+str(name)+'_0'\n",
-    "    Y_str_0 = 'y_'+str(name)+'_0'\n",
-    "    X_str_1 = 'x_'+str(name)+'_1'\n",
-    "    Y_str_1 = 'y_'+str(name)+'_1'\n",
+    "def get_dynamics_contract_robot(name, init_pos, timestep):\n",
+    "    X_str_0 = \"x_\" + str(name) + \"_0\"\n",
+    "    Y_str_0 = \"y_\" + str(name) + \"_0\"\n",
+    "    X_str_1 = \"x_\" + str(name) + \"_1\"\n",
+    "    Y_str_1 = \"y_\" + str(name) + \"_1\"\n",
     "    t_0 = \"t_0\"\n",
     "    t_1 = \"t_1\"\n",
     "\n",
     "    contract = PolyhedralContract.from_string(\n",
-    "    input_vars = [X_str_0, Y_str_0, t_0],\n",
-    "    output_vars= [X_str_1, Y_str_1, t_1],\n",
-    "    assumptions=[\n",
-    "      f\"{X_str_0} = \" + str(init_pos.x),\n",
-    "      f\"{Y_str_0} = \" + str(init_pos.y),\n",
-    "      f\"{t_0} = \" + str(timestep),\n",
-    "    ],\n",
-    "    guarantees=[\n",
-    "      f\"{t_1} - {t_0} = 1\",\n",
-    "      f\"{X_str_1} - {X_str_0} + {Y_str_1} - {Y_str_0} <= 1\",\n",
-    "      f\"{X_str_1} - {X_str_0} - {Y_str_1} + {Y_str_0} <= 1\",\n",
-    "      f\"-{X_str_1} + {X_str_0} + {Y_str_1} - {Y_str_0} <= 1\",\n",
-    "      f\"-{X_str_1} + {X_str_0} - {Y_str_1} + {Y_str_0} <= 1\",\n",
-    "      f\"{X_str_1} <= {n-1}\",\n",
-    "      f\"{Y_str_1} <= {m-1}\",\n",
-    "      f\"-{X_str_1} <= 0\",\n",
-    "      f\"-{Y_str_1} <= 0\",\n",
-    "    ]\n",
-    "  )\n",
+    "        input_vars=[X_str_0, Y_str_0, t_0],\n",
+    "        output_vars=[X_str_1, Y_str_1, t_1],\n",
+    "        assumptions=[\n",
+    "            f\"{X_str_0} = \" + str(init_pos.x),\n",
+    "            f\"{Y_str_0} = \" + str(init_pos.y),\n",
+    "            f\"{t_0} = \" + str(timestep),\n",
+    "        ],\n",
+    "        guarantees=[\n",
+    "            f\"{t_1} - {t_0} = 1\",\n",
+    "            f\"{X_str_1} - {X_str_0} + {Y_str_1} - {Y_str_0} <= 1\",\n",
+    "            f\"{X_str_1} - {X_str_0} - {Y_str_1} + {Y_str_0} <= 1\",\n",
+    "            f\"-{X_str_1} + {X_str_0} + {Y_str_1} - {Y_str_0} <= 1\",\n",
+    "            f\"-{X_str_1} + {X_str_0} - {Y_str_1} + {Y_str_0} <= 1\",\n",
+    "            f\"{X_str_1} <= {n-1}\",\n",
+    "            f\"{Y_str_1} <= {m-1}\",\n",
+    "            f\"-{X_str_1} <= 0\",\n",
+    "            f\"-{Y_str_1} <= 0\",\n",
+    "        ],\n",
+    "    )\n",
     "    return contract"
    ]
   },
@@ -235,23 +235,21 @@
    "outputs": [],
    "source": [
     "def collision_quadrant_1():\n",
-    "    \n",
     "    contract = PolyhedralContract.from_string(\n",
-    "    input_vars = [\"t_0\", \"current_distance\"],\n",
-    "    output_vars= [\"x_A_1\", \"y_A_1\", \"x_B_1\", \"y_B_1\", \"t_1\"]\n",
-    "                 ,\n",
-    "    assumptions=[\n",
-    "      f\"-current_distance <= -1\",\n",
-    "    ],\n",
-    "    guarantees=[\n",
-    "      f\"x_A_1 - x_B_1 + y_A_1 - y_B_1 <= -1\",\n",
-    "    ]\n",
+    "        input_vars=[\"t_0\", \"current_distance\"],\n",
+    "        output_vars=[\"x_A_1\", \"y_A_1\", \"x_B_1\", \"y_B_1\", \"t_1\"],\n",
+    "        assumptions=[\n",
+    "            f\"-current_distance <= -1\",\n",
+    "        ],\n",
+    "        guarantees=[\n",
+    "            f\"x_A_1 - x_B_1 + y_A_1 - y_B_1 <= -1\",\n",
+    "        ],\n",
     "    )\n",
-    "    \n",
+    "\n",
     "    return contract\n",
     "\n",
-    "from utils.contract_utils import collision_quadrant_2, collision_quadrant_3, \\\n",
-    "collision_quadrant_4"
+    "\n",
+    "from utils.contract_utils import collision_quadrant_2, collision_quadrant_3, collision_quadrant_4"
    ]
   },
   {
@@ -324,8 +322,8 @@
     "c_q3 = collision_quadrant_3()\n",
     "c_q4 = collision_quadrant_4()\n",
     "\n",
-    "print(\"Contract A:\\n\" + str(c_dyn1)+'\\n')\n",
-    "print(\"Contract B:\\n\" + str(c_dyn2)+'\\n')\n",
+    "print(\"Contract A:\\n\" + str(c_dyn1) + \"\\n\")\n",
+    "print(\"Contract B:\\n\" + str(c_dyn2) + \"\\n\")\n",
     "\n",
     "print(\"Collision Quadrant Q1:\\n\" + str(c_q1))"
    ]
@@ -372,20 +370,19 @@
    ],
    "source": [
     "def dynamic_collision_constraints():\n",
-    "    \n",
     "    contract = PolyhedralContract.from_string(\n",
-    "    input_vars = [\"delta_x\", \"delta_y\", \"current_distance\"],\n",
-    "    output_vars= []\n",
-    "                 ,\n",
-    "    assumptions=[\n",
-    "      f\"-current_distance <= -1\",\n",
-    "    ],\n",
-    "    guarantees=[\n",
-    "      f\"-delta_x - delta_y <= -1\",\n",
-    "    ]\n",
+    "        input_vars=[\"delta_x\", \"delta_y\", \"current_distance\"],\n",
+    "        output_vars=[],\n",
+    "        assumptions=[\n",
+    "            f\"-current_distance <= -1\",\n",
+    "        ],\n",
+    "        guarantees=[\n",
+    "            f\"-delta_x - delta_y <= -1\",\n",
+    "        ],\n",
     "    )\n",
     "    return contract\n",
     "\n",
+    "\n",
     "c_dyn_collision = dynamic_collision_constraints()\n",
     "\n",
     "print(\"Dynamic Collision Contract:\\n\" + str(c_dyn_collision))"
@@ -509,7 +506,6 @@
    ],
    "source": [
     "def find_move_candidates(r1, r2, T_0, contract, c_dyn_collision):\n",
-    "    \n",
     "    x_A_0 = Var(\"x_A_0\")\n",
     "    y_A_0 = Var(\"y_A_0\")\n",
     "    x_B_0 = Var(\"x_B_0\")\n",
@@ -523,59 +519,68 @@
     "    y_B_1 = Var(\"y_B_1\")\n",
     "    delta_x = Var(\"delta_x\")\n",
     "    delta_y = Var(\"delta_y\")\n",
-    "    \n",
+    "\n",
     "    X_A_0 = r1.pos.x\n",
     "    Y_A_0 = r1.pos.y\n",
-    "    X_B_0 = r2.pos.x \n",
+    "    X_B_0 = r2.pos.x\n",
     "    Y_B_0 = r2.pos.y\n",
     "    cur_dist = np.abs(X_A_0 - X_B_0) + np.abs(Y_A_0 - Y_B_0)\n",
     "\n",
     "    T_1 = T_0 + 1\n",
-    "    \n",
+    "\n",
     "    # find possible next position [(x,y),(x,y)] options for robots\n",
     "    possible_sol = []\n",
-    "                    \n",
-    "    for x_a in [max(X_A_0-1,0), X_A_0, min(X_A_0+1,n)]:\n",
-    "        for y_a in [max(Y_A_0-1,0), Y_A_0, min(Y_A_0+1,m)]:\n",
-    "            for x_b in [max(X_B_0-1,0), X_B_0, min(X_B_0+1,n)]:\n",
-    "                for y_b in [max(Y_B_0-1,0), Y_B_0, min(Y_B_0+1,m)]:\n",
-    "                    \n",
-    "                    del_x = (x_a - x_b)*(X_A_0 - X_B_0)\n",
-    "                    del_y = (y_a - y_b)*(Y_A_0 - Y_B_0)\n",
-    "                \n",
-    "                    \n",
-    "                    var_dict = {x_A_0: X_A_0, y_A_0: Y_A_0, x_B_0: X_B_0, \\\n",
-    "                               y_B_0: Y_B_0, current_distance: cur_dist, \\\n",
-    "                               t_0: T_0, t_1: T_1, x_A_1: x_a, y_A_1: y_a, \\\n",
-    "                               x_B_1: x_b, y_B_1: y_b, delta_x: del_x, \\\n",
-    "                               delta_y: del_y}\n",
-    "                    \n",
-    "                    if c_dyn_collision.a.contains_behavior(var_dict) and \\\n",
-    "                         c_dyn_collision.g.contains_behavior(var_dict) and \\\n",
-    "                         contract.a.contains_behavior(var_dict) and \\\n",
-    "                         contract.g.contains_behavior(var_dict):\n",
-    "                        \n",
-    "                        possible_sol.append([(x_a,y_a),(x_b,y_b)])\n",
-    "                        \n",
+    "\n",
+    "    for x_a in [max(X_A_0 - 1, 0), X_A_0, min(X_A_0 + 1, n)]:\n",
+    "        for y_a in [max(Y_A_0 - 1, 0), Y_A_0, min(Y_A_0 + 1, m)]:\n",
+    "            for x_b in [max(X_B_0 - 1, 0), X_B_0, min(X_B_0 + 1, n)]:\n",
+    "                for y_b in [max(Y_B_0 - 1, 0), Y_B_0, min(Y_B_0 + 1, m)]:\n",
+    "                    del_x = (x_a - x_b) * (X_A_0 - X_B_0)\n",
+    "                    del_y = (y_a - y_b) * (Y_A_0 - Y_B_0)\n",
+    "\n",
+    "                    var_dict = {\n",
+    "                        x_A_0: X_A_0,\n",
+    "                        y_A_0: Y_A_0,\n",
+    "                        x_B_0: X_B_0,\n",
+    "                        y_B_0: Y_B_0,\n",
+    "                        current_distance: cur_dist,\n",
+    "                        t_0: T_0,\n",
+    "                        t_1: T_1,\n",
+    "                        x_A_1: x_a,\n",
+    "                        y_A_1: y_a,\n",
+    "                        x_B_1: x_b,\n",
+    "                        y_B_1: y_b,\n",
+    "                        delta_x: del_x,\n",
+    "                        delta_y: del_y,\n",
+    "                    }\n",
+    "\n",
+    "                    if (\n",
+    "                        c_dyn_collision.a.contains_behavior(var_dict)\n",
+    "                        and c_dyn_collision.g.contains_behavior(var_dict)\n",
+    "                        and contract.a.contains_behavior(var_dict)\n",
+    "                        and contract.g.contains_behavior(var_dict)\n",
+    "                    ):\n",
+    "                        possible_sol.append([(x_a, y_a), (x_b, y_b)])\n",
+    "\n",
     "    return possible_sol, t_1\n",
     "\n",
+    "\n",
     "t_0 = 0\n",
     "sols = []\n",
     "for i in range(len(merged_contracts)):\n",
-    "    possible_sol, t_1 = find_move_candidates(r1, r2, t_0, merged_contracts[i], \\\n",
-    "                                             c_dyn_collision)\n",
+    "    possible_sol, t_1 = find_move_candidates(r1, r2, t_0, merged_contracts[i], c_dyn_collision)\n",
     "    sols = sols + possible_sol\n",
     "\n",
     "move_candidates = []\n",
     "for sol in sols:\n",
     "    if sol not in move_candidates:\n",
     "        move_candidates.append(sol)\n",
-    " \n",
+    "\n",
     "# now pick a move and run it again for the next timestep!\n",
-    "move = strategy(move_candidates,(r1.goal,r2.goal))\n",
-    "print('These are the possible positions for the next timestep: ' + str(move_candidates))\n",
+    "move = strategy(move_candidates, (r1.goal, r2.goal))\n",
+    "print(\"These are the possible positions for the next timestep: \" + str(move_candidates))\n",
     "\n",
-    "print('The chosen move is '+str(move))"
+    "print(\"The chosen move is \" + str(move))"
    ]
   },
   {
@@ -983,45 +988,45 @@
     }
    ],
    "source": [
-    "r1 = Robot('A', (0,0), (5,2))\n",
-    "r2 = Robot('B', (0,2), (5,0))\n",
+    "r1 = Robot(\"A\", (0, 0), (5, 2))\n",
+    "r2 = Robot(\"B\", (0, 2), (5, 0))\n",
     "trace = []\n",
-    "robots = [r1,r2]\n",
+    "robots = [r1, r2]\n",
     "\n",
     "t = 0\n",
-    "trace = save_trace(trace, [(r1.pos.x, r1.pos.y),(r2.pos.x, r2.pos.y)], \\\n",
-    "                   [(r1.goal.x, r1.goal.y),(r2.goal.x, r2.goal.y)])\n",
+    "trace = save_trace(\n",
+    "    trace, [(r1.pos.x, r1.pos.y), (r2.pos.x, r2.pos.y)], [(r1.goal.x, r1.goal.y), (r2.goal.x, r2.goal.y)]\n",
+    ")\n",
     "\n",
-    "for i in range(0,15):\n",
+    "for i in range(0, 15):\n",
     "    c_dyn1 = get_dynamics_contract_robot(r1.name, r1.pos, t)\n",
     "    c_dyn2 = get_dynamics_contract_robot(r2.name, r2.pos, t)\n",
     "    merged_dyn_contract = c_dyn1.merge(c_dyn2)\n",
-    "    merged_contracts = check_collision_quadrants(merged_dyn_contract, c_q1, c_q2, \\\n",
-    "                                                 c_q3, c_q4)\n",
-    "    \n",
+    "    merged_contracts = check_collision_quadrants(merged_dyn_contract, c_q1, c_q2, c_q3, c_q4)\n",
+    "\n",
     "    sols = []\n",
     "    for i in range(len(merged_contracts)):\n",
-    "        possible_sol, t_1 = find_move_candidates(r1, r2, t, merged_contracts[i], \\\n",
-    "                                                 c_dyn_collision)\n",
+    "        possible_sol, t_1 = find_move_candidates(r1, r2, t, merged_contracts[i], c_dyn_collision)\n",
     "        sols = sols + possible_sol\n",
     "\n",
     "    move_candidates = []\n",
     "    for sol in sols:\n",
     "        if sol not in move_candidates:\n",
     "            move_candidates.append(sol)\n",
-    "            \n",
-    "    move = strategy(move_candidates,(r1.goal,r2.goal))\n",
+    "\n",
+    "    move = strategy(move_candidates, (r1.goal, r2.goal))\n",
     "    r1.move(move[0])\n",
     "    r2.move(move[1])\n",
-    "    \n",
-    "    trace = save_trace(trace, [(r1.pos.x, r1.pos.y),(r2.pos.x, r2.pos.y)], \\\n",
-    "                       [(r1.goal.x, r1.goal.y),(r2.goal.x, r2.goal.y)])\n",
-    "    \n",
-    "    if distance([[r1.pos.x, r1.pos.y],[r2.pos.x, r2.pos.y]],[r1.goal,r2.goal]) == 0:\n",
-    "        break;\n",
+    "\n",
+    "    trace = save_trace(\n",
+    "        trace, [(r1.pos.x, r1.pos.y), (r2.pos.x, r2.pos.y)], [(r1.goal.x, r1.goal.y), (r2.goal.x, r2.goal.y)]\n",
+    "    )\n",
+    "\n",
+    "    if distance([[r1.pos.x, r1.pos.y], [r2.pos.x, r2.pos.y]], [r1.goal, r2.goal]) == 0:\n",
+    "        break\n",
     "#     t = t_1\n",
-    "        \n",
-    "animate(trace, n, m, '2_robots')"
+    "\n",
+    "animate(trace, n, m, \"2_robots\")"
    ]
   },
   {
@@ -1500,63 +1505,71 @@
     }
    ],
    "source": [
-    "from utils.multiagent_utils import strategy_multiple, find_move_candidates_multiple,\\\n",
-    "    get_possible_moves_multiple_robots, get_collision_contracts_robot_pair\n",
+    "from utils.multiagent_utils import (\n",
+    "    get_collision_contracts_robot_pair,\n",
+    "    get_possible_moves_multiple_robots,\n",
+    "    strategy_multiple,\n",
+    ")\n",
     "\n",
     "# set grid world dimensions n x m\n",
     "n = 5\n",
     "m = 5\n",
     "\n",
     "# Intitialize the robots\n",
-    "r1 = Robot('A', (0,0), (4,4))\n",
-    "r2 = Robot('B', (0,2), (4,2))\n",
-    "r3 = Robot('C', (0,4), (4,0))\n",
-    "robots = [r1,r2,r3]\n",
+    "r1 = Robot(\"A\", (0, 0), (4, 4))\n",
+    "r2 = Robot(\"B\", (0, 2), (4, 2))\n",
+    "r3 = Robot(\"C\", (0, 4), (4, 0))\n",
+    "robots = [r1, r2, r3]\n",
     "\n",
     "# Get the collision contracts\n",
-    "collision_AB = get_collision_contracts_robot_pair('A', 'B', 'C')\n",
-    "collision_AC = get_collision_contracts_robot_pair('A', 'C', 'B')\n",
-    "collision_BC = get_collision_contracts_robot_pair('B', 'C', 'A')\n",
+    "collision_AB = get_collision_contracts_robot_pair(\"A\", \"B\", \"C\")\n",
+    "collision_AC = get_collision_contracts_robot_pair(\"A\", \"C\", \"B\")\n",
+    "collision_BC = get_collision_contracts_robot_pair(\"B\", \"C\", \"A\")\n",
     "collision_contracts = [collision_AB, collision_AC, collision_BC]\n",
     "c_dyn_collision = get_dynamic_collision_contract(robots)\n",
     "\n",
     "t = 0\n",
     "trace = []\n",
-    "trace = save_trace(trace, [(r1.pos.x, r1.pos.y),(r2.pos.x, r2.pos.y),(r3.pos.x, r3.pos.y)],\\\n",
-    "                   [(r1.goal.x, r1.goal.y),(r2.goal.x, r2.goal.y),(r3.goal.x, r3.goal.y)])\n",
+    "trace = save_trace(\n",
+    "    trace,\n",
+    "    [(r1.pos.x, r1.pos.y), (r2.pos.x, r2.pos.y), (r3.pos.x, r3.pos.y)],\n",
+    "    [(r1.goal.x, r1.goal.y), (r2.goal.x, r2.goal.y), (r3.goal.x, r3.goal.y)],\n",
+    ")\n",
     "last_pos = None\n",
     "\n",
-    "for i in range(0,30):\n",
+    "for i in range(0, 30):\n",
     "    c_dyn1 = get_dynamics_contract_robot(r1.name, r1.pos, t)\n",
     "    c_dyn2 = get_dynamics_contract_robot(r2.name, r2.pos, t)\n",
     "    c_dyn3 = get_dynamics_contract_robot(r3.name, r3.pos, t)\n",
-    "    \n",
+    "\n",
     "    merged_dyn_contract_1_2 = c_dyn1.merge(c_dyn2)\n",
     "    merged_dyn_contract = merged_dyn_contract_1_2.merge(c_dyn3)\n",
-    "    \n",
-    "    moves_list = get_possible_moves_multiple_robots(n,m,robots, merged_dyn_contract, \\\n",
-    "                                                    collision_contracts, c_dyn_collision)\n",
-    "    \n",
-    "    goals = (r1.goal,r2.goal,r3.goal)\n",
-    "    cur_pos = (r1.pos.x, r1.pos.y),(r2.pos.x, r2.pos.y),(r3.pos.x, r3.pos.y)\n",
-    "    cur_dist = distance(cur_pos,goals)\n",
+    "\n",
+    "    moves_list = get_possible_moves_multiple_robots(\n",
+    "        n, m, robots, merged_dyn_contract, collision_contracts, c_dyn_collision\n",
+    "    )\n",
+    "\n",
+    "    goals = (r1.goal, r2.goal, r3.goal)\n",
+    "    cur_pos = (r1.pos.x, r1.pos.y), (r2.pos.x, r2.pos.y), (r3.pos.x, r3.pos.y)\n",
+    "    cur_dist = distance(cur_pos, goals)\n",
     "\n",
     "    move = strategy_multiple(moves_list, goals, cur_pos, last_pos)\n",
     "    last_pos = cur_pos\n",
-    "    \n",
+    "\n",
     "    r1.move(move[0])\n",
     "    r2.move(move[1])\n",
     "    r3.move(move[2])\n",
-    "    \n",
-    "    trace = save_trace(trace, [(r1.pos.x, r1.pos.y),(r2.pos.x, r2.pos.y),\\\n",
-    "                              (r3.pos.x, r3.pos.y)], [(r1.goal.x, r1.goal.y),\\\n",
-    "                              (r2.goal.x, r2.goal.y), (r3.goal.x, r3.goal.y)])\n",
-    "    \n",
-    "    if distance([[r1.pos.x, r1.pos.y],[r2.pos.x, r2.pos.y],[r3.pos.x, r3.pos.y]],\\\n",
-    "                [r1.goal,r2.goal, r3.goal]) == 0:\n",
-    "        break;\n",
-    "        \n",
-    "animate(trace, n, m, '3_robots')"
+    "\n",
+    "    trace = save_trace(\n",
+    "        trace,\n",
+    "        [(r1.pos.x, r1.pos.y), (r2.pos.x, r2.pos.y), (r3.pos.x, r3.pos.y)],\n",
+    "        [(r1.goal.x, r1.goal.y), (r2.goal.x, r2.goal.y), (r3.goal.x, r3.goal.y)],\n",
+    "    )\n",
+    "\n",
+    "    if distance([[r1.pos.x, r1.pos.y], [r2.pos.x, r2.pos.y], [r3.pos.x, r3.pos.y]], [r1.goal, r2.goal, r3.goal]) == 0:\n",
+    "        break\n",
+    "\n",
+    "animate(trace, n, m, \"3_robots\")"
    ]
   },
   {
diff --git a/case_studies/multiagent_coordination/utils/contract_utils.py b/case_studies/multiagent_coordination/utils/contract_utils.py
index 3850914f..5cc04e23 100644
--- a/case_studies/multiagent_coordination/utils/contract_utils.py
+++ b/case_studies/multiagent_coordination/utils/contract_utils.py
@@ -1,45 +1,43 @@
 from pacti.terms.polyhedra import PolyhedralContract
 
+
 def collision_quadrant_2():
     contract = PolyhedralContract.from_string(
-    input_vars = ["x_A_0", "y_A_0","x_B_0", "y_B_0", "t_0", "current_distance"],
-    output_vars= ["x_A_1", "y_A_1", "x_B_1", "y_B_1", "t_1"]
-                 ,
-    assumptions=[
-      f"-current_distance <= -1",
-    ],
-    guarantees=[
-      f"- x_A_1 + x_B_1 - y_A_1 + y_B_1 <= -1",
-    ]
+        input_vars=["x_A_0", "y_A_0", "x_B_0", "y_B_0", "t_0", "current_distance"],
+        output_vars=["x_A_1", "y_A_1", "x_B_1", "y_B_1", "t_1"],
+        assumptions=[
+            f"-current_distance <= -1",
+        ],
+        guarantees=[
+            f"- x_A_1 + x_B_1 - y_A_1 + y_B_1 <= -1",
+        ],
     )
     return contract
 
 
 def collision_quadrant_3():
     contract = PolyhedralContract.from_string(
-    input_vars = ["x_A_0", "y_A_0","x_B_0", "y_B_0", "t_0", "current_distance"],
-    output_vars= ["x_A_1", "y_A_1", "x_B_1", "y_B_1", "t_1"]
-                 ,
-    assumptions=[
-      f"-current_distance <= -1",
-    ],
-    guarantees=[
-      f"x_A_1 - x_B_1 - y_A_1 + y_B_1 <= -1",
-    ]
+        input_vars=["x_A_0", "y_A_0", "x_B_0", "y_B_0", "t_0", "current_distance"],
+        output_vars=["x_A_1", "y_A_1", "x_B_1", "y_B_1", "t_1"],
+        assumptions=[
+            f"-current_distance <= -1",
+        ],
+        guarantees=[
+            f"x_A_1 - x_B_1 - y_A_1 + y_B_1 <= -1",
+        ],
     )
     return contract
 
 
 def collision_quadrant_4():
     contract = PolyhedralContract.from_string(
-    input_vars = ["x_A_0", "y_A_0","x_B_0", "y_B_0", "t_0", "current_distance"],
-    output_vars= ["x_A_1", "y_A_1", "x_B_1", "y_B_1", "t_1"]
-                 ,
-    assumptions=[
-      f"-current_distance <= -1",
-    ],
-    guarantees=[
-      f"- x_A_1 + x_B_1 + y_A_1 - y_B_1 <= -1",
-    ]
+        input_vars=["x_A_0", "y_A_0", "x_B_0", "y_B_0", "t_0", "current_distance"],
+        output_vars=["x_A_1", "y_A_1", "x_B_1", "y_B_1", "t_1"],
+        assumptions=[
+            f"-current_distance <= -1",
+        ],
+        guarantees=[
+            f"- x_A_1 + x_B_1 + y_A_1 - y_B_1 <= -1",
+        ],
     )
     return contract
diff --git a/case_studies/multiagent_coordination/utils/multiagent_utils.py b/case_studies/multiagent_coordination/utils/multiagent_utils.py
index d907c5b4..14d29ada 100644
--- a/case_studies/multiagent_coordination/utils/multiagent_utils.py
+++ b/case_studies/multiagent_coordination/utils/multiagent_utils.py
@@ -1,8 +1,11 @@
 import random
 from itertools import combinations
+
 import numpy as np
-from pacti.terms.polyhedra import PolyhedralContract
+
 from pacti.iocontract import Var
+from pacti.terms.polyhedra import PolyhedralContract
+
 
 # coordinate class
 class Coord:
@@ -33,8 +36,9 @@ def distance(candidate, goal):
 
     return distance
 
+
 def indiv_distance(move, goal):
-    distance = np.abs(move[0]-goal.x)+np.abs(move[1]-goal.y)
+    distance = np.abs(move[0] - goal.x) + np.abs(move[1] - goal.y)
     return distance
 
 
@@ -58,6 +62,7 @@ def strategy(move_candidates, goal):
     move = random.choice(min_dist[min(sorted(min_dist.keys()))])
     return move
 
+
 def strategy_multiple_simple(move_candidates, goal):
     """
     Function to return a chosen move for both robots,
@@ -85,13 +90,13 @@ def strategy_multiple_simple(move_candidates, goal):
 
 
 def strategy_multiple(move_candidates, goal, cur_pos, last_pos):
-    '''
+    """
     Function to return a chosen move for all robots.
-    '''
+    """
     min_dist = {}
     for candidate in move_candidates:
         candidate_list = []
-        dist = distance(candidate,goal)
+        dist = distance(candidate, goal)
 
         if dist in min_dist.keys():
             for entry in min_dist[dist]:
@@ -102,8 +107,8 @@ def strategy_multiple(move_candidates, goal, cur_pos, last_pos):
 
     best_move = random.choice(min_dist[min(sorted(min_dist.keys()))])
 
-    best_dist = distance(best_move,goal)
-    cur_dist = distance(cur_pos,goal)
+    best_dist = distance(best_move, goal)
+    cur_dist = distance(cur_pos, goal)
 
     if best_dist < cur_dist:
         move_options = []
@@ -114,41 +119,45 @@ def strategy_multiple(move_candidates, goal, cur_pos, last_pos):
         moves = []
         for move in move_options:
             move_ok = True
-            for i in range(len(cur_pos)): # keep robots at goal at goal
-                if indiv_distance(cur_pos[i],goal[i]) < indiv_distance(move[i],goal[i]):#indiv_distance(cur_pos[i],goal[i]) < indiv_distance(move[i],goal[i]):
+            for i in range(len(cur_pos)):  # keep robots at goal at goal
+                if indiv_distance(cur_pos[i], goal[i]) < indiv_distance(
+                    move[i], goal[i]
+                ):  # indiv_distance(cur_pos[i],goal[i]) < indiv_distance(move[i],goal[i]):
                     move_ok = False
             if move_ok and move not in moves:
                 moves = moves + [move]
 
-        if random.random() < 0.1: # take the best move 10% of the time
+        if random.random() < 0.1:  # take the best move 10% of the time
             move = random.choice(min_dist[min(sorted(min_dist.keys()))])
-        else: # take a random good move
+        else:  # take a random good move
             move = random.choice(moves)
     # check that other moves are possible
     elif best_dist == cur_dist:
         moves = []
         for move in move_candidates:
             move_ok = True
-            if move == cur_pos: # remove staying in the same position
+            if move == cur_pos:  # remove staying in the same position
                 move_ok = False
-            if move == last_pos: # remove going back to previous position
+            if move == last_pos:  # remove going back to previous position
                 move_ok = False
-            for i in range(len(cur_pos)): # keep robots at goal at goal
-                if indiv_distance(cur_pos[i],goal[i]) == 0 and indiv_distance(move[i],goal[i]) != 0:#indiv_distance(cur_pos[i],goal[i]) < indiv_distance(move[i],goal[i]):
+            for i in range(len(cur_pos)):  # keep robots at goal at goal
+                if (
+                    indiv_distance(cur_pos[i], goal[i]) == 0 and indiv_distance(move[i], goal[i]) != 0
+                ):  # indiv_distance(cur_pos[i],goal[i]) < indiv_distance(move[i],goal[i]):
                     move_ok = False
-            if move_ok and move not in moves: # add move to possible moves
+            if move_ok and move not in moves:  # add move to possible moves
                 moves = moves + [move]
 
-        move = random.choice(moves) # take a random move
+        move = random.choice(moves)  # take a random move
 
     return move
 
 
 def check_collision_quadrants(merged_dyn_contract, c_q1, c_q2, c_q3, c_q4):
-    '''
+    """
     Merge each of the collision contracts with
     the dynamics contract.
-    '''
+    """
     merged_contracts = []
     try:
         merged_contract_q1 = merged_dyn_contract.merge(c_q1)
@@ -177,10 +186,10 @@ def check_collision_quadrants(merged_dyn_contract, c_q1, c_q2, c_q3, c_q4):
 
 
 def find_move_candidates_multiple(n, m, robots, T_0, contract, c_dyn_collision):
-    '''
+    """
     Evaluate the contracts for possible next positions
     of the robots to find allowed moves.
-    '''
+    """
     x_A_0 = Var("x_A_0")
     y_A_0 = Var("y_A_0")
     x_B_0 = Var("x_B_0")
@@ -217,13 +226,12 @@ def find_move_candidates_multiple(n, m, robots, T_0, contract, c_dyn_collision):
 
     # find possible [(x,y),(x,y)] options for robots
     possible_sol = []
-    for x_a in [max(X_A_0-1,0), X_A_0, min(X_A_0+1,n)]:
-        for y_a in [max(Y_A_0-1,0), Y_A_0, min(Y_A_0+1,m)]:
-            for x_b in [max(X_B_0-1,0), X_B_0, min(X_B_0+1,n)]:
-                for y_b in [max(Y_B_0-1,0), Y_B_0, min(Y_B_0+1,m)]:
-                    for x_c in [max(X_C_0-1,0), X_C_0, min(X_C_0+1,n)]:
-                        for y_c in [max(Y_C_0-1,0), Y_C_0, min(Y_C_0+1,m)]:
-
+    for x_a in [max(X_A_0 - 1, 0), X_A_0, min(X_A_0 + 1, n)]:
+        for y_a in [max(Y_A_0 - 1, 0), Y_A_0, min(Y_A_0 + 1, m)]:
+            for x_b in [max(X_B_0 - 1, 0), X_B_0, min(X_B_0 + 1, n)]:
+                for y_b in [max(Y_B_0 - 1, 0), Y_B_0, min(Y_B_0 + 1, m)]:
+                    for x_c in [max(X_C_0 - 1, 0), X_C_0, min(X_C_0 + 1, n)]:
+                        for y_c in [max(Y_C_0 - 1, 0), Y_C_0, min(Y_C_0 + 1, m)]:
                             del_x_A_B = (x_a - x_b) * (X_A_0 - X_B_0)
                             del_y_A_B = (y_a - y_b) * (Y_A_0 - Y_B_0)
                             del_x_A_C = (x_a - x_c) * (X_A_0 - X_C_0)
@@ -231,23 +239,39 @@ def find_move_candidates_multiple(n, m, robots, T_0, contract, c_dyn_collision):
                             del_x_B_C = (x_b - x_c) * (X_B_0 - X_C_0)
                             del_y_B_C = (y_b - y_c) * (Y_B_0 - Y_C_0)
 
-                            var_dict = {x_A_0: X_A_0, y_A_0: Y_A_0, x_B_0: X_B_0, \
-                               y_B_0: Y_B_0, x_C_0: X_C_0, y_C_0: Y_C_0, \
-                               current_distance: cur_dist, \
-                               t_0: T_0, t_1: T_1, x_A_1: x_a, y_A_1: y_a, \
-                               x_B_1: x_b, y_B_1: y_b, x_C_1: x_c, y_C_1: y_c,\
-                               delta_x_A_B: del_x_A_B, delta_y_A_B: del_y_A_B, \
-                               delta_x_A_C: del_x_A_C, delta_y_A_C: del_y_A_C, \
-                               delta_x_B_C: del_x_B_C, delta_y_B_C: del_y_B_C}
+                            var_dict = {
+                                x_A_0: X_A_0,
+                                y_A_0: Y_A_0,
+                                x_B_0: X_B_0,
+                                y_B_0: Y_B_0,
+                                x_C_0: X_C_0,
+                                y_C_0: Y_C_0,
+                                current_distance: cur_dist,
+                                t_0: T_0,
+                                t_1: T_1,
+                                x_A_1: x_a,
+                                y_A_1: y_a,
+                                x_B_1: x_b,
+                                y_B_1: y_b,
+                                x_C_1: x_c,
+                                y_C_1: y_c,
+                                delta_x_A_B: del_x_A_B,
+                                delta_y_A_B: del_y_A_B,
+                                delta_x_A_C: del_x_A_C,
+                                delta_y_A_C: del_y_A_C,
+                                delta_x_B_C: del_x_B_C,
+                                delta_y_B_C: del_y_B_C,
+                            }
 
                             # from ipdb import set_trace as st
                             # st()
-                            if c_dyn_collision.a.contains_behavior(var_dict) and \
-                                c_dyn_collision.g.contains_behavior(var_dict) and \
-                                contract.a.contains_behavior(var_dict) and \
-                                contract.g.contains_behavior(var_dict):
-
-                                possible_sol.append([(x_a,y_a),(x_b,y_b),(x_c,y_c)])
+                            if (
+                                c_dyn_collision.a.contains_behavior(var_dict)
+                                and c_dyn_collision.g.contains_behavior(var_dict)
+                                and contract.a.contains_behavior(var_dict)
+                                and contract.g.contains_behavior(var_dict)
+                            ):
+                                possible_sol.append([(x_a, y_a), (x_b, y_b), (x_c, y_c)])
 
                             # sol = True
                             # for g in c_dyn_collision.g.terms:
@@ -269,20 +293,20 @@ def find_move_candidates_multiple(n, m, robots, T_0, contract, c_dyn_collision):
 
 
 def robots_move(robots, move):
-    '''
+    """
     Apply next move and update positions.
-    '''
+    """
     for i in range(len(robots)):
         robots[i].move(move[i])
 
 
 def get_possible_moves_multiple_robots(n, m, robots, merged_dyn_contract, collision_contracts, c_dyn_collision):
-    '''
+    """
     Iterate though the robot pairs, merge each collision
     contract with the dynamics contract and find the
     possible moves for that robot pair.
     Returns moves that are allowed for all robot pairs.
-    '''
+    """
     t_0 = 0
     move_options = []
     for contract_list in collision_contracts:
@@ -309,9 +333,9 @@ def get_possible_moves_multiple_robots(n, m, robots, merged_dyn_contract, collis
 
 
 def get_dynamic_collision_contract(robots):
-    '''
+    """
     Contract ensureing no swapping conflicts for all robots.
-    '''
+    """
     robotnames = []
     for robot in robots:
         robotnames.append(robot.name)
diff --git a/case_studies/multiagent_coordination/utils/plotting_utils.py b/case_studies/multiagent_coordination/utils/plotting_utils.py
index bd8a3507..4f503f8c 100644
--- a/case_studies/multiagent_coordination/utils/plotting_utils.py
+++ b/case_studies/multiagent_coordination/utils/plotting_utils.py
@@ -1,8 +1,7 @@
-import matplotlib
 import matplotlib.pyplot as plt
-from matplotlib.animation import FuncAnimation
 import numpy as np
 from IPython.display import HTML
+from matplotlib.animation import FuncAnimation
 from matplotlib.patches import Circle, Rectangle
 from utils.multiagent_utils import Coord
 
diff --git a/case_studies/uav_topologies_generation/src/contracts_utils/union.py b/case_studies/uav_topologies_generation/src/contracts_utils/union.py
index e32a914a..37831df5 100644
--- a/case_studies/uav_topologies_generation/src/contracts_utils/union.py
+++ b/case_studies/uav_topologies_generation/src/contracts_utils/union.py
@@ -23,14 +23,14 @@ class ContractsUnions:
     name: str = ""
 
     def get_inverted(self) -> ContractsUnions:
-
         inverted_contracts = ContractsUnions(name=self.name)
 
         for contract in self.contracts:
             from pacti.terms.polyhedra import PolyhedralTermList
-            new_contract = IoContract(assumptions=contract.g,
-                                      guarantees=PolyhedralTermList(), output_vars=[],
-                                      input_vars=contract.outputvars)
+
+            new_contract = IoContract(
+                assumptions=contract.g, guarantees=PolyhedralTermList(), output_vars=[], input_vars=contract.outputvars
+            )
             inverted_contracts.contracts.add(new_contract)
 
         return inverted_contracts
@@ -73,9 +73,8 @@ def __le__(self, other: IoContract | ContractsUnions) -> bool:
         return True
 
     def __str__(self):
-
-        assumptions = [str(c.a).replace('in', '').replace('1*', '') for c in self.contracts]
-        guarantees = [str(c.g).replace('in', '').replace('1*', '') for c in self.contracts]
+        assumptions = [str(c.a).replace("in", "").replace("1*", "") for c in self.contracts]
+        guarantees = [str(c.g).replace("in", "").replace("1*", "") for c in self.contracts]
         a = "\t | \t".join(assumptions)
         g = "\t | \t".join(guarantees)
         return f"A: {a}\nG: {g}"
@@ -124,13 +123,13 @@ def can_be_composed_with_old(self, other: ContractsUnions):
                 try:
                     c = c_self.compose(c_other)
                     print("\n")
-                    print(str(c_self.a).replace('in', '').replace('1*', ''))
-                    print(str(c_other.g).replace('in', '').replace('1*', ''))
+                    print(str(c_self.a).replace("in", "").replace("1*", ""))
+                    print(str(c_other.g).replace("in", "").replace("1*", ""))
                     print("TRUE")
                 except Exception as e:
                     print("\n")
-                    print(str(c_self.a).replace('in', '').replace('1*', ''))
-                    print(str(c_other.g).replace('in', '').replace('1*', ''))
+                    print(str(c_self.a).replace("in", "").replace("1*", ""))
+                    print(str(c_other.g).replace("in", "").replace("1*", ""))
                     print("FALSE")
                     next = True
                     break
diff --git a/case_studies/uav_topologies_generation/src/grammar/figures.py b/case_studies/uav_topologies_generation/src/grammar/figures.py
index eae1bfa4..dae6e1e4 100644
--- a/case_studies/uav_topologies_generation/src/grammar/figures.py
+++ b/case_studies/uav_topologies_generation/src/grammar/figures.py
@@ -1,8 +1,8 @@
 from dataclasses import dataclass
 
 import numpy as np
-from ..shared import direction_to_coordinates
 
+from ..shared import direction_to_coordinates
 from ..tools.plotting import plot_3d_grid
 
 
diff --git a/case_studies/uav_topologies_generation/src/grammar/grammar.py b/case_studies/uav_topologies_generation/src/grammar/grammar.py
index 806cefe4..2d96539f 100644
--- a/case_studies/uav_topologies_generation/src/grammar/grammar.py
+++ b/case_studies/uav_topologies_generation/src/grammar/grammar.py
@@ -5,9 +5,9 @@
 from dataclasses import dataclass
 from pathlib import Path
 
-from .rule import Rule
 from ..contracts_utils.union import ContractsUnions
 from ..shared import SymbolType
+from .rule import Rule
 
 
 @dataclass
diff --git a/case_studies/uav_topologies_generation/src/grammar/grid.py b/case_studies/uav_topologies_generation/src/grammar/grid.py
index c25c22a7..7b90c680 100644
--- a/case_studies/uav_topologies_generation/src/grammar/grid.py
+++ b/case_studies/uav_topologies_generation/src/grammar/grid.py
@@ -7,12 +7,13 @@
 
 from case_studies.uav_topologies_generation.src.contracts_utils.union import ContractsUnions
 from pacti.terms.polyhedra import PolyhedralContract
-from .figures import DirectionsGrid
-from ..tools.plotting import plot_3d_grid
-from .rule import Rule
-from .symbols import Symbol, Unoccupied, Fuselage, Empty, Rotor, Wing, Connector
+
 from ..shared import Direction, SymbolType, symbols_colors
 from ..tools.constraints import from_symbol_directions_to_constraints
+from ..tools.plotting import plot_3d_grid
+from .figures import DirectionsGrid
+from .rule import Rule
+from .symbols import Connector, Empty, Fuselage, Rotor, Symbol, Unoccupied, Wing
 
 
 @dataclass
@@ -140,11 +141,8 @@ def contract(self) -> ContractsUnions:
             # io_contract = string_to_polyhedra_contract(new_c)
 
             io_contract = PolyhedralContract.from_string(
-                output_vars=list(symbols),
-                input_vars=[],
-                assumptions=[],
-                guarantees=constraint)
-
+                output_vars=list(symbols), input_vars=[], assumptions=[], guarantees=constraint
+            )
 
             contract_union.contracts.add(io_contract)
         return contract_union
@@ -453,8 +451,8 @@ def connect_points(src: Point, dest: Point):
         unoccupied_points = set(
             filter(
                 lambda p: isinstance(self.grid[p], Unoccupied)
-                          and (abs(p.x) < edge and abs(p.y) < edge and abs(p.z) < edge)
-                          and p not in self.points_visited,
+                and (abs(p.x) < edge and abs(p.y) < edge and abs(p.z) < edge)
+                and p not in self.points_visited,
                 [p for p in self.current_point.symmetry_directions],
             )
         )
diff --git a/case_studies/uav_topologies_generation/src/grammar/rule.py b/case_studies/uav_topologies_generation/src/grammar/rule.py
index 71f8964c..2620e059 100644
--- a/case_studies/uav_topologies_generation/src/grammar/rule.py
+++ b/case_studies/uav_topologies_generation/src/grammar/rule.py
@@ -7,10 +7,11 @@
 from matplotlib.figure import Figure
 
 from pacti.terms.polyhedra import PolyhedralContract
-from .figures import DirectionsGrid
+
 from ..contracts_utils.union import ContractsUnions
 from ..shared import Direction, SymbolType, symbols_colors
 from ..tools.constraints import from_symbol_directions_to_constraints
+from .figures import DirectionsGrid
 
 
 @dataclass
@@ -27,14 +28,12 @@ def from_dict(cls, rule_dict: dict, name: str = "rule") -> Rule:
             connection = Direction[rule_dict["production"]["connection"]]
         symbol_name = rule_dict["production"]["ego"]
 
-        pr = Production(symbol_type=getattr(SymbolType, symbol_name),
-                        connection=connection)
+        pr = Production(symbol_type=getattr(SymbolType, symbol_name), connection=connection)
 
         return Rule(conditions=cs, production=pr, name=name)
 
     @property
     def contract(self) -> ContractsUnions:
-
         symbols_dirs = self.conditions.get_all_symbol_types_and_directions()
         constraints, symbols = from_symbol_directions_to_constraints(symbols_dirs)
 
@@ -48,10 +47,8 @@ def contract(self) -> ContractsUnions:
             print(constraint)
             print(list(symbols))
             io_contract = PolyhedralContract.from_string(
-                input_vars=list(symbols),
-                output_vars=[],
-                assumptions=constraint,
-                guarantees=[])
+                input_vars=list(symbols), output_vars=[], assumptions=constraint, guarantees=[]
+            )
 
             contract_union.contracts.add(io_contract)
 
@@ -131,8 +128,7 @@ def get_all_conditions(self) -> list[Condition]:
         # for elem in res:
         #     print(elem)
         for f, b, l, ri, t, re in itertools.product(
-                *[list(self.front), list(self.bottom), list(self.left), list(self.right), list(self.top),
-                  list(self.rear)]
+            *[list(self.front), list(self.bottom), list(self.left), list(self.right), list(self.top), list(self.rear)]
         ):
             conditions.append(Condition(f, b, l, ri, t, re))
         return conditions
diff --git a/case_studies/uav_topologies_generation/src/grammar/symbols.py b/case_studies/uav_topologies_generation/src/grammar/symbols.py
index d724263b..7c25e429 100644
--- a/case_studies/uav_topologies_generation/src/grammar/symbols.py
+++ b/case_studies/uav_topologies_generation/src/grammar/symbols.py
@@ -9,6 +9,7 @@
 @dataclass(frozen=True)
 class Symbol:
     """General Symbol class"""
+
     symbol_type = SymbolType.UNOCCUPIED
 
     @property
diff --git a/case_studies/uav_topologies_generation/src/main.py b/case_studies/uav_topologies_generation/src/main.py
index f2a10ccc..eceb163b 100644
--- a/case_studies/uav_topologies_generation/src/main.py
+++ b/case_studies/uav_topologies_generation/src/main.py
@@ -14,7 +14,6 @@
 max_num_rotors = 2
 
 if __name__ == "__main__":
-
     grammar = Grammar.from_json(rules_json_path=rules_path)
 
     directions_assignment = DirectionsAssignment()
@@ -76,7 +75,7 @@
         else:
             rules_compatible_contracts = rule_matching(current_state.contract, rules_allowed_contracts)
         end = datetime.now()
-        time_contracts = (end - start).total_seconds() * 10 ** 6
+        time_contracts = (end - start).total_seconds() * 10**6
         #
         # print(time_search)
         # print(time_contracts)
@@ -120,4 +119,3 @@
     grid.plot_with_edges.savefig("before.pdf")
     grid.cleanup()
     grid.plot_with_edges.savefig("after.pdf")
-    pass
diff --git a/case_studies/uav_topologies_generation/src/shared/__init__.py b/case_studies/uav_topologies_generation/src/shared/__init__.py
index 734f163d..33e88001 100644
--- a/case_studies/uav_topologies_generation/src/shared/__init__.py
+++ b/case_studies/uav_topologies_generation/src/shared/__init__.py
@@ -1,1 +1,1 @@
-from .shared import *
\ No newline at end of file
+from .shared import *
diff --git a/case_studies/uav_topologies_generation/src/tools/constraints.py b/case_studies/uav_topologies_generation/src/tools/constraints.py
index 561dfae1..0a11e175 100644
--- a/case_studies/uav_topologies_generation/src/tools/constraints.py
+++ b/case_studies/uav_topologies_generation/src/tools/constraints.py
@@ -24,7 +24,7 @@ def constraint_str_less_eq_than(symbol: str, n: int) -> str:
 
 
 def from_symbol_directions_to_constraints(
-        symbol_dirs: dict[SymbolType, set[Direction]]
+    symbol_dirs: dict[SymbolType, set[Direction]]
 ) -> tuple[list[list[str]], set[str]]:
     """
     Convert a dictionary of symbol_types -> directions into linear constraints
@@ -62,8 +62,6 @@ def from_symbol_directions_to_constraints(
     for or_constraint in or_constraints:
         constraints.append([item for sublist in or_constraint for item in sublist])
 
-
-
     return constraints, symbols
 
     # or_assumptions = list(itertools.product(*assumptions))
diff --git a/case_studies/uav_topologies_generation/src/tools/plotting.py b/case_studies/uav_topologies_generation/src/tools/plotting.py
index 8d218020..b2bf557a 100644
--- a/case_studies/uav_topologies_generation/src/tools/plotting.py
+++ b/case_studies/uav_topologies_generation/src/tools/plotting.py
@@ -3,6 +3,7 @@
 from matplotlib.figure import Figure
 from numpy.typing import NDArray
 
+
 def generate_empty_grid(n_half: int):
     node_xyz = []
     color_xyz = []
@@ -54,10 +55,10 @@ def plot_3d_grid(
     return fig
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     # g = generate_empty_grid(1)
     # g.savefig("empty_1", format="svg")
     new_grid = GridBuilder.generate(half_size=1)
     # new_grid.plot.show()
     current_node_state = new_grid.local_state(new_grid.current_point)
-    current_node_state.plot.savefig("local_state", format="svg")
\ No newline at end of file
+    current_node_state.plot.savefig("local_state", format="svg")
diff --git a/case_studies/uav_topologies_generation/topologies.ipynb b/case_studies/uav_topologies_generation/topologies.ipynb
index 66ad0157..dc30cc66 100644
--- a/case_studies/uav_topologies_generation/topologies.ipynb
+++ b/case_studies/uav_topologies_generation/topologies.ipynb
@@ -309,18 +309,15 @@
     }
    ],
    "source": [
-    "\n",
     "import random\n",
-    "from datetime import datetime\n",
-    "from pacti.utils.contracts_union import ContractsUnions\n",
     "\n",
     "from src.grammar.grammar import Grammar\n",
     "from src.grammar.grid import GridBuilder\n",
     "from src.shared import DirectionsAssignment, SymbolType\n",
     "from src.shared.paths import rules_path\n",
     "from src.tools.refinement_checking import rule_matching\n",
-    "from src.tools.simple_rule_search import set_rules_search\n",
-    "from pacti.utils.contracts_union import ContractsUnions\n"
+    "\n",
+    "from pacti.utils.contracts_union import ContractsUnions"
    ]
   },
   {
@@ -437,13 +434,12 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "step=0\n",
+    "step = 0\n",
     "\n",
     "while len(grid.points_to_visit) > 0:\n",
     "    print(f\"STEP {step}\")\n",
     "    print(f\"{len(grid.points_to_visit)} POINTS LEFT:\\t{grid.points_to_visit}\")\n",
     "\n",
-    "\n",
     "    \"\"\"Update the current point to visit\"\"\"\n",
     "    grid.update_current_point()\n",
     "\n",
@@ -456,7 +452,6 @@
     "    \"\"\"Plot the current state\"\"\"\n",
     "    current_state.plot.show()\n",
     "\n",
-    "\n",
     "    \"\"\"Create the list of \"rule contracts\" that are allowed at the current iteration based on the current number of wings and propellers\"\"\"\n",
     "    forbidden_symbols = set()\n",
     "\n",
diff --git a/docs/case_studies.md b/docs/case_studies.md
index 6aa6172a..bec568c4 100644
--- a/docs/case_studies.md
+++ b/docs/case_studies.md
@@ -20,4 +20,7 @@ A case study on modeling the specifications of biological sensors to construct a
 
 <font size="5">[**Signal processing pipelines in digital ICs**](/pacti/_case_studies/digital_signal_processing/dsp_wl/)</font>
 
+A case study on word length analysis and optimization for digital signal processing circuit design.
+<img src="https://github.com/FormalSystems/media/blob/main/case_studies/digital_signal_processing/digital_filter_flow.png?raw=True" alt= "DSP figure" width="700"/>
+
 <font size="5">**Generating UAV topologies**</font>
diff --git a/docs/getting_started.ipynb b/docs/getting_started.ipynb
index ec7d13e4..6c65fb8b 100644
--- a/docs/getting_started.ipynb
+++ b/docs/getting_started.ipynb
@@ -66,16 +66,12 @@
     "from pacti.terms.polyhedra import PolyhedralContract\n",
     "\n",
     "contract1 = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"i\"],\n",
-    "    output_vars=[\"o\"],\n",
-    "    assumptions=[\"|i| <= 2\"],\n",
-    "    guarantees=[\"o - i <= 0\", \"i - 2o <= 2\"])\n",
+    "    input_vars=[\"i\"], output_vars=[\"o\"], assumptions=[\"|i| <= 2\"], guarantees=[\"o - i <= 0\", \"i - 2o <= 2\"]\n",
+    ")\n",
     "\n",
     "contract2 = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"o\"],\n",
-    "    output_vars=[\"o_p\"],\n",
-    "    assumptions=[\"o <= 0.2\", \"-o <= 1\"],\n",
-    "    guarantees=[\"o_p - o <= 0\"])\n",
+    "    input_vars=[\"o\"], output_vars=[\"o_p\"], assumptions=[\"o <= 0.2\", \"-o <= 1\"], guarantees=[\"o_p - o <= 0\"]\n",
+    ")\n",
     "\n",
     "system_contract = contract1.compose(contract2)\n",
     "print(system_contract)"
@@ -126,10 +122,8 @@
    ],
    "source": [
     "contract1_n = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"i\"],\n",
-    "    output_vars=[\"o\"],\n",
-    "    assumptions=[\"|i| <= 2\"],\n",
-    "    guarantees=[\"|o| <= 3\"])\n",
+    "    input_vars=[\"i\"], output_vars=[\"o\"], assumptions=[\"|i| <= 2\"], guarantees=[\"|o| <= 3\"]\n",
+    ")\n",
     "\n",
     "\n",
     "try:\n",
@@ -195,16 +189,12 @@
    ],
    "source": [
     "contract_top_level = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"i\"],\n",
-    "    output_vars=[\"o_p\"],\n",
-    "    assumptions=[\"|i| <= 1\"],\n",
-    "    guarantees=[\"o_p - 2i = 1\"])\n",
+    "    input_vars=[\"i\"], output_vars=[\"o_p\"], assumptions=[\"|i| <= 1\"], guarantees=[\"o_p - 2i = 1\"]\n",
+    ")\n",
     "\n",
     "contract_existing_subsystem = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"i\"],\n",
-    "    output_vars=[\"o\"],\n",
-    "    assumptions=[\"|i| <= 2\"],\n",
-    "    guarantees=[\"o - 2i = 0\"])\n",
+    "    input_vars=[\"i\"], output_vars=[\"o\"], assumptions=[\"|i| <= 2\"], guarantees=[\"o - 2i = 0\"]\n",
+    ")\n",
     "\n",
     "contract_missing_subsystem = contract_top_level.quotient(contract_existing_subsystem)\n",
     "print(contract_missing_subsystem)"
@@ -232,7 +222,7 @@
     }
    ],
    "source": [
-    "# compose quotient \n",
+    "# compose quotient\n",
     "new_system = contract_missing_subsystem.compose(contract_existing_subsystem)\n",
     "print(new_system.refines(contract_top_level))"
    ]
@@ -278,16 +268,12 @@
    ],
    "source": [
     "functionality_viewpoint = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"i\"],\n",
-    "    output_vars=[\"o\"],\n",
-    "    assumptions=[\"|i| <= 2\"],\n",
-    "    guarantees=[\"o - 2i = 1\"])\n",
+    "    input_vars=[\"i\"], output_vars=[\"o\"], assumptions=[\"|i| <= 2\"], guarantees=[\"o - 2i = 1\"]\n",
+    ")\n",
     "\n",
     "power_viewpoint = PolyhedralContract.from_string(\n",
-    "    input_vars=[\"temp\"],\n",
-    "    output_vars=[\"P\"],\n",
-    "    assumptions=[\"temp <= 90\"],\n",
-    "    guarantees=[\"P <= 2.1\"])\n",
+    "    input_vars=[\"temp\"], output_vars=[\"P\"], assumptions=[\"temp <= 90\"], guarantees=[\"P <= 2.1\"]\n",
+    ")\n",
     "\n",
     "subsystem_contract = functionality_viewpoint.compose(power_viewpoint)\n",
     "print(subsystem_contract)"
diff --git a/duties.py b/duties.py
index bfb102c1..792c0f98 100644
--- a/duties.py
+++ b/duties.py
@@ -32,13 +32,15 @@
 
 from duty import duty
 
-PY_SRC_PATHS = (Path(_) for _ in ("src", "tests", "docs"))
+PY_SRC_PATHS = (Path(_) for _ in ("src", "tests", "case_studies"))
 PY_SRC_LIST = tuple(str(_) for _ in PY_SRC_PATHS)
 PY_SRC = " ".join(PY_SRC_LIST)
+JNB_SRC = " ".join(glob.glob("**/*.ipynb", recursive=True))
 TESTING = os.environ.get("TESTING", "0") in {"1", "true"}
 CI = os.environ.get("CI", "0") in {"1", "true", "yes", ""}
 WINDOWS = os.name == "nt"
 PTY = not WINDOWS and not CI
+MYPY_FLAGS = "--allow-any-generics --implicit-reexport --allow-untyped-calls"
 
 
 def _latest(lines: List[str], regex: Pattern) -> Optional[str]:
@@ -147,6 +149,17 @@ def check_quality(ctx, files=PY_SRC):
     """Latest Flake8 cause problems with dependencies. Suppress for now."""
     ctx.run(f"flake8 --config=config/flake8.ini {files}", title="Checking code quality", pty=PTY)
 
+@duty  # noqa: WPS231
+def check_jn_quality(ctx):  # noqa: WPS231
+    """
+    Check notebook quality.
+
+    Arguments:
+        ctx: The context instance (passed automatically).
+        files: The files to check.
+    """
+    """Latest Flake8 cause problems with dependencies. Suppress for now."""
+    ctx.run(f"nbqa flake8 --config=config/flake8.ini {JNB_SRC}", title="Type checking notebooks", pty=PTY)
 
 @duty
 def tox(ctx):
@@ -218,6 +231,7 @@ def check_docs(ctx):
     """
     Path("htmlcov").mkdir(parents=True, exist_ok=True)
     Path("htmlcov/index.html").touch(exist_ok=True)
+    copy_case_studies(ctx)
     ctx.run("mkdocs build -s", title="Building documentation", pty=PTY)
 
 
@@ -229,7 +243,18 @@ def check_types(ctx):  # noqa: WPS231
     Arguments:
         ctx: The context instance (passed automatically).
     """
-    ctx.run(f"mypy --config-file config/mypy.ini {PY_SRC}", title="Type-checking", pty=PTY)
+    ctx.run(f"mypy --strict {MYPY_FLAGS} --config-file=config/mypy.ini {PY_SRC}", title="Type-checking", pty=PTY)
+
+
+@duty  # noqa: WPS231
+def check_jn_types(ctx):  # noqa: WPS231
+    """
+    Check that notebooks are correctly typed.
+
+    Arguments:
+        ctx: The context instance (passed automatically).
+    """
+    ctx.run(f"nbqa mypy --strict {MYPY_FLAGS} --config-file=config/mypy.ini {JNB_SRC}", title="Type checking notebooks", pty=PTY)
 
 
 @duty(silent=True)
@@ -285,6 +310,7 @@ def docs(ctx):
     Arguments:
         ctx: The context instance (passed automatically).
     """
+    copy_case_studies(ctx)
     ctx.run("mkdocs build", title="Building documentation")
 
 
@@ -329,6 +355,14 @@ def format(ctx):
     )
     ctx.run(f"isort {PY_SRC}", title="Ordering imports", pty=PTY)
     ctx.run(f"black {PY_SRC}", title="Formatting code", pty=PTY)
+    ############
+    ctx.run(
+        f"nbqa autoflake -ir --ignore-init-module-imports --remove-all-unused-imports {PY_SRC}",
+        title="Removing unused imports",
+        pty=PTY,
+    )
+    ctx.run(f"nbqa isort {PY_SRC}", title="Ordering imports", pty=PTY)
+    ctx.run(f"nbqa black {PY_SRC}", title="Formatting code", pty=PTY)
 
 
 @duty
diff --git a/examples/wip/polyhedra_contracts/polyhedral-term-examples.py b/examples/wip/polyhedra_contracts/polyhedral-term-examples.py
index f9d7df51..722da987 100644
--- a/examples/wip/polyhedra_contracts/polyhedral-term-examples.py
+++ b/examples/wip/polyhedra_contracts/polyhedral-term-examples.py
@@ -1,5 +1,5 @@
 import re
-from pacti.terms.polyhedra.serializer import internal_pt_from_string
+from pacti.terms.polyhedra.serializer import polyhedral_termlist_from_string
 
 terms=["-t1 <= -1.0", 
        "t1<=4", 
@@ -22,5 +22,5 @@
        "-3.0e1 e - x+z = 3.0e-1"]
 for t in terms:
   print(f"\nterm: {t}")
-  p=internal_pt_from_string(t)
+  p=polyhedral_termlist_from_string(t)
   print(p)
diff --git a/pyproject.toml b/pyproject.toml
index 489494c3..dfc907b5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -73,6 +73,7 @@ quality = [
     "flake8-variables-names",
     "pep8-naming",
     "wps-light",
+    "nbqa>=1.6.3",
 ]
 tests = [
     "pytest>=6.2",
diff --git a/src/pacti/iocontract/compundiocontract.py b/src/pacti/iocontract/compundiocontract.py
index a95e1173..c3c641e4 100644
--- a/src/pacti/iocontract/compundiocontract.py
+++ b/src/pacti/iocontract/compundiocontract.py
@@ -5,7 +5,7 @@
 import logging
 from typing import List, TypeVar, Union
 
-from pacti.iocontract.iocontract import TL_t, Var
+from pacti.iocontract.iocontract import TermList_t, Var
 from pacti.utils.lists import list_diff, list_intersection, list_union
 
 NTL_t = TypeVar("NTL_t", bound="NestedTermList")
@@ -16,7 +16,7 @@ class NestedTermList:
     """A collection of termlists interpreted as their disjunction."""
 
     def __init__(  # noqa: WPS231 too much cognitive complexity
-        self, nested_termlist: list[TL_t], force_empty_intersection: bool
+        self, nested_termlist: list[TermList_t], force_empty_intersection: bool
     ):
         """
         Class constructor.
@@ -36,7 +36,7 @@ def __init__(  # noqa: WPS231 too much cognitive complexity
                         intersection = tli | tlj
                         if not intersection.is_empty():
                             raise ValueError("Terms %s and %s have nonempty intersection" % (tli, tlj))
-        self.nested_termlist: list[TL_t] = []
+        self.nested_termlist: list[TermList_t] = []
         for tl in nested_termlist:
             self.nested_termlist.append(tl.copy())
 
@@ -46,12 +46,13 @@ def __str__(self) -> str:
             return "\nor \n".join(res)
         return "true"
 
-    def simplify(self: NTL_t, context: NTL_t):
+    def simplify(self: NTL_t, context: NTL_t, force_empty_intersection: bool) -> None:
         """
         Remove redundant terms in nested termlist.
 
         Args:
             context: Nested termlist serving as context for simplification.
+            force_empty_intersection: Make sure the resulting termlists have empty intersection.
         """
         new_nested_tl = []
         for self_tl in self.nested_termlist:
@@ -62,7 +63,7 @@ def simplify(self: NTL_t, context: NTL_t):
                 except ValueError:
                     continue
                 new_nested_tl.append(new_tl)
-        self.nested_termlist = type(self)(new_nested_tl).nested_termlist
+        self.nested_termlist = type(self)(new_nested_tl, force_empty_intersection).nested_termlist
 
     def intersect(self: NTL_t, other: NTL_t, force_empty_intersection: bool) -> NTL_t:
         """
@@ -201,7 +202,7 @@ def __init__(self, assumptions: NTL_t, guarantees: NTL_t, input_vars: List[Var],
         # simplify the guarantees with the assumptions
         # self.g.simplify(self.a)
 
-    def __str__(self):
+    def __str__(self) -> str:
         return (
             "InVars: "
             + "["
diff --git a/src/pacti/iocontract/iocontract.py b/src/pacti/iocontract/iocontract.py
index bbc6f96d..2e8fb611 100644
--- a/src/pacti/iocontract/iocontract.py
+++ b/src/pacti/iocontract/iocontract.py
@@ -25,6 +25,11 @@
 from pacti.utils.errors import IncompatibleArgsError
 from pacti.utils.lists import list_diff, list_intersection, list_union, lists_equal
 
+Var_t = TypeVar("Var_t", bound="Var")
+Term_t = TypeVar("Term_t", bound="Term")
+TermList_t = TypeVar("TermList_t", bound="TermList")
+IoContract_t = TypeVar("IoContract_t", bound="IoContract")
+
 
 class Var:
     """
@@ -51,7 +56,9 @@ def name(self) -> str:
         """
         return self._name
 
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, type(self)):
+            raise ValueError()
         return self.name == other.name
 
     def __str__(self) -> str:
@@ -60,7 +67,7 @@ def __str__(self) -> str:
     def __hash__(self) -> int:
         return hash(self.name)
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return "<Var {0}>".format(self.name)
 
 
@@ -74,7 +81,7 @@ class Term(ABC):
 
     @property
     @abstractmethod
-    def vars(self):  # noqa: A003
+    def vars(self) -> list[Var]:  # noqa: A003
         """Variables contained in the syntax of the term."""
 
     @abstractmethod
@@ -87,7 +94,7 @@ def contains_var(self, var_to_seek: Var) -> bool:
         """
 
     @abstractmethod
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
         pass
 
     @abstractmethod
@@ -95,19 +102,19 @@ def __str__(self) -> str:
         pass
 
     @abstractmethod
-    def __hash__(self):
+    def __hash__(self) -> int:
         pass
 
     @abstractmethod
-    def __repr__(self):
+    def __repr__(self) -> str:
         pass
 
     @abstractmethod
-    def copy(self):
+    def copy(self: Term_t) -> Term_t:
         """Returns a copy of term."""
 
     @abstractmethod
-    def rename_variable(self, source_var: Var, target_var: Var):
+    def rename_variable(self: Term_t, source_var: Var, target_var: Var) -> Term_t:
         """
         Rename a variable in a term.
 
@@ -120,9 +127,6 @@ def rename_variable(self, source_var: Var, target_var: Var):
         """
 
 
-TL_t = TypeVar("TL_t", bound="TermList")
-
-
 class TermList(ABC):
     """
     A collection of terms, or constraints.
@@ -162,10 +166,12 @@ def __str__(self) -> str:
             return ", ".join(res)
         return "true"
 
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, type(self)):
+            raise ValueError()
         return self.terms == other.terms
 
-    def get_terms_with_vars(self: TL_t, variable_list: List[Var]) -> TL_t:
+    def get_terms_with_vars(self: TermList_t, variable_list: List[Var]) -> TermList_t:
         """
         Returns the list of terms which contain any of the variables indicated.
 
@@ -181,19 +187,19 @@ def get_terms_with_vars(self: TL_t, variable_list: List[Var]) -> TL_t:
                 terms.append(t)
         return type(self)(terms)
 
-    def __and__(self, other):
+    def __and__(self: TermList_t, other: TermList_t) -> TermList_t:
         return type(self)(list_intersection(self.copy().terms, other.copy().terms))
 
-    def __or__(self, other):
+    def __or__(self: TermList_t, other: TermList_t) -> TermList_t:
         return type(self)(list_union(self.copy().terms, other.copy().terms))
 
-    def __sub__(self, other):
+    def __sub__(self: TermList_t, other: TermList_t) -> TermList_t:
         return type(self)(list_union(self.copy().terms, other.copy().terms))
 
-    def __le__(self, other):
+    def __le__(self: TermList_t, other: TermList_t) -> bool:
         return self.refines(other)
 
-    def copy(self: TL_t) -> TL_t:
+    def copy(self: TermList_t) -> TermList_t:
         """
         Makes copy of termlist.
 
@@ -202,7 +208,7 @@ def copy(self: TL_t) -> TL_t:
         """
         return type(self)([term.copy() for term in self.terms])
 
-    def rename_variable(self: TL_t, source_var: Var, target_var: Var) -> TL_t:
+    def rename_variable(self: TermList_t, source_var: Var, target_var: Var) -> TermList_t:
         """
         Rename a variable in a termlist.
 
@@ -232,7 +238,7 @@ def contains_behavior(self, behavior: Any) -> bool:
         """
 
     @abstractmethod
-    def elim_vars_by_refining(self: TL_t, context: TL_t, vars_to_elim: List[Var]) -> TL_t:
+    def elim_vars_by_refining(self: TermList_t, context: TermList_t, vars_to_elim: List[Var]) -> TermList_t:
         """
         Eliminate variables from termlist by refining it in a context.
 
@@ -254,7 +260,7 @@ def elim_vars_by_refining(self: TL_t, context: TL_t, vars_to_elim: List[Var]) ->
         """
 
     @abstractmethod
-    def elim_vars_by_relaxing(self: TL_t, context: TL_t, vars_to_elim: List[Var]) -> TL_t:
+    def elim_vars_by_relaxing(self: TermList_t, context: TermList_t, vars_to_elim: List[Var]) -> TermList_t:
         """
         Eliminate variables from termlist by relaxing it in a context
 
@@ -276,7 +282,7 @@ def elim_vars_by_relaxing(self: TL_t, context: TL_t, vars_to_elim: List[Var]) ->
         """
 
     @abstractmethod
-    def simplify(self: TL_t, context: Optional[TL_t] = None):
+    def simplify(self: TermList_t, context: Optional[TermList_t] = None) -> None:
         """Remove redundant terms in TermList.
 
         Let $S$ be this TermList and suppose $T \\subseteq S$. Let
@@ -291,7 +297,7 @@ def simplify(self: TL_t, context: Optional[TL_t] = None):
         """
 
     @abstractmethod
-    def refines(self: TL_t, other: TL_t) -> bool:
+    def refines(self: TermList_t, other: TermList_t) -> bool:
         """
         Tell whether the argument is a larger specification.
 
@@ -313,10 +319,7 @@ def is_empty(self) -> bool:
         """
 
 
-Cont_t = TypeVar("Cont_t", bound="IoContract")
-
-
-class IoContract(Generic[TL_t]):
+class IoContract(Generic[TermList_t]):
     """
     Basic type for an IO contract.
 
@@ -332,7 +335,9 @@ class IoContract(Generic[TL_t]):
         g(TermList): Contract guarantees.
     """
 
-    def __init__(self, assumptions: TL_t, guarantees: TL_t, input_vars: List[Var], output_vars: List[Var]) -> None:
+    def __init__(
+        self, assumptions: TermList_t, guarantees: TermList_t, input_vars: List[Var], output_vars: List[Var]
+    ) -> None:
         """
         Class constructor.
 
@@ -376,8 +381,8 @@ def __init__(self, assumptions: TL_t, guarantees: TL_t, input_vars: List[Var], o
                 % (list_diff(guarantees.vars, list_union(input_vars, output_vars)), input_vars, output_vars, guarantees)
             )
 
-        self.a: TL_t = assumptions.copy()
-        self.g: TL_t = guarantees.copy()
+        self.a: TermList_t = assumptions.copy()
+        self.g: TermList_t = guarantees.copy()
         self.inputvars = input_vars.copy()
         self.outputvars = output_vars.copy()
         # simplify the guarantees with the assumptions
@@ -393,7 +398,7 @@ def vars(self) -> list[Var]:  # noqa: A003
         """
         return list_union(self.inputvars, self.outputvars)
 
-    def __str__(self):
+    def __str__(self) -> str:
         return (
             "InVars: "
             + "["
@@ -410,7 +415,9 @@ def __str__(self):
             + str(self.g)
         )
 
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, type(self)):
+            raise ValueError
         return (
             self.inputvars == other.inputvars
             and self.outputvars == self.outputvars
@@ -419,8 +426,8 @@ def __eq__(self, other):
         )
 
     def rename_variable(  # noqa: WPS231 too much cognitive complexity
-        self: Cont_t, source_var: Var, target_var: Var
-    ) -> Cont_t:
+        self: IoContract_t, source_var: Var, target_var: Var
+    ) -> IoContract_t:
         """
         Rename a variable in a contract.
 
@@ -457,7 +464,7 @@ def rename_variable(  # noqa: WPS231 too much cognitive complexity
                 guarantees = guarantees.rename_variable(source_var, target_var)
         return type(self)(assumptions, guarantees, inputvars, outputvars)
 
-    def copy(self: Cont_t) -> Cont_t:
+    def copy(self: IoContract_t) -> IoContract_t:
         """
         Makes copy of contract.
 
@@ -470,10 +477,12 @@ def copy(self: Cont_t) -> Cont_t:
         guarantees = self.g.copy()
         return type(self)(assumptions, guarantees, inputvars, outputvars)
 
-    def __le__(self, other):
+    def __le__(self, other: object) -> bool:
+        if not isinstance(other, type(self)):
+            raise ValueError()
         return self.refines(other)
 
-    def can_compose_with(self: Cont_t, other: Cont_t) -> bool:
+    def can_compose_with(self: IoContract_t, other: IoContract_t) -> bool:
         """
         Tell whether the contract can be composed with another contract.
 
@@ -488,7 +497,7 @@ def can_compose_with(self: Cont_t, other: Cont_t) -> bool:
         # make sure lists of output variables don't intersect
         return len(list_intersection(self.outputvars, other.outputvars)) == 0
 
-    def can_quotient_by(self: Cont_t, other: Cont_t) -> bool:
+    def can_quotient_by(self: IoContract_t, other: IoContract_t) -> bool:
         """
         Tell whether the contract can quotiented by another contract.
 
@@ -504,7 +513,7 @@ def can_quotient_by(self: Cont_t, other: Cont_t) -> bool:
         # component
         return len(list_intersection(list_diff(self.outputvars, other.outputvars), other.inputvars)) == 0
 
-    def shares_io_with(self: Cont_t, other: Cont_t) -> bool:
+    def shares_io_with(self: IoContract_t, other: IoContract_t) -> bool:
         """
         Tell whether two contracts have the same IO signature.
 
@@ -516,7 +525,7 @@ def shares_io_with(self: Cont_t, other: Cont_t) -> bool:
         """
         return lists_equal(self.inputvars, other.inputvars) & lists_equal(self.outputvars, other.outputvars)
 
-    def refines(self: Cont_t, other: Cont_t) -> bool:
+    def refines(self: IoContract_t, other: IoContract_t) -> bool:
         """
         Tell whether the given contract is a refinement of another.
 
@@ -533,9 +542,11 @@ def refines(self: Cont_t, other: Cont_t) -> bool:
         """
         if not self.shares_io_with(other):
             raise IncompatibleArgsError("Contracts do not share IO")
-        return (other.a <= self.a) and ((self.g | other.a) <= (other.g | other.a))
+        assumptions_check: bool = other.a <= self.a
+        guarantees_check: bool = (self.g | other.a) <= (other.g | other.a)
+        return assumptions_check and guarantees_check
 
-    def compose(self: Cont_t, other: Cont_t, vars_to_keep: Any = None) -> Cont_t:  # noqa: WPS231, WPS238
+    def compose(self: IoContract_t, other: IoContract_t, vars_to_keep: Any = None) -> IoContract_t:  # noqa: WPS231
         """Compose IO contracts.
 
         Compute the composition of the two given contracts and abstract the
@@ -592,7 +603,7 @@ def compose(self: Cont_t, other: Cont_t, vars_to_keep: Any = None) -> Cont_t:  #
             raise IncompatibleArgsError("Cannot compose contracts due to feedback")
         elif self_helps_other and not other_helps_self:
             logging.debug("Assumption computation: self provides context for other")
-            new_a: TL_t = other.a.elim_vars_by_refining(self.a | self.g, assumptions_forbidden_vars)
+            new_a: TermList_t = other.a.elim_vars_by_refining(self.a | self.g, assumptions_forbidden_vars)
             conflict_variables = list_intersection(new_a.vars, assumptions_forbidden_vars)
             if conflict_variables:
                 raise IncompatibleArgsError(
@@ -635,7 +646,9 @@ def compose(self: Cont_t, other: Cont_t, vars_to_keep: Any = None) -> Cont_t:  #
 
         return type(self)(assumptions, allguarantees, inputvars, outputvars)
 
-    def quotient(self: Cont_t, other: Cont_t, additional_inputs: Optional[List[Var]] = None) -> Cont_t:
+    def quotient(
+        self: IoContract_t, other: IoContract_t, additional_inputs: Optional[List[Var]] = None
+    ) -> IoContract_t:
         """Compute the contract quotient.
 
         Compute the quotient self/other of the two given contracts and refine
@@ -688,7 +701,7 @@ def quotient(self: Cont_t, other: Cont_t, additional_inputs: Optional[List[Var]]
 
         # get guarantees
         logging.debug("Computing quotient guarantees")
-        guarantees: TL_t = self.g
+        guarantees: TermList_t = self.g
         logging.debug("Using existing guarantees to aid system-level guarantees")
         try:
             guarantees = guarantees.elim_vars_by_refining(other.g | other.a, intvars)
@@ -711,7 +724,7 @@ def quotient(self: Cont_t, other: Cont_t, additional_inputs: Optional[List[Var]]
 
         return type(self)(assumptions, guarantees, inputvars, outputvars)
 
-    def merge(self: Cont_t, other: Cont_t) -> Cont_t:
+    def merge(self: IoContract_t, other: IoContract_t) -> IoContract_t:
         """
         Compute the merging operation for two contracts.
 
@@ -727,7 +740,7 @@ def merge(self: Cont_t, other: Cont_t) -> Cont_t:
         Raises:
             IncompatibleArgsError: trying to merge different contract types.
         """
-        if isinstance(self, type(other)):
+        if not isinstance(self, type(other)):
             raise IncompatibleArgsError("Asked to merge incompatible contracts")
         input_vars = list_union(self.inputvars, other.inputvars)
         output_vars = list_union(self.outputvars, other.outputvars)
diff --git a/src/pacti/terms/polyhedra/polyhedra.py b/src/pacti/terms/polyhedra/polyhedra.py
index fb84d661..9870a3f7 100644
--- a/src/pacti/terms/polyhedra/polyhedra.py
+++ b/src/pacti/terms/polyhedra/polyhedra.py
@@ -8,7 +8,7 @@
 from __future__ import annotations
 
 import logging
-from typing import Any, Tuple, Union
+from typing import Any, Optional, Tuple, Union
 
 import numpy as np
 import sympy
@@ -48,18 +48,23 @@ def __init__(self, variables: dict[Var, numeric], constant: numeric):
         Args:
             variables: A dictionary mapping Var keys to numeric values.
             constant: A numeric value on the right of the inequality.
+
+        Raises:
+            ValueError: Unsupported argument type.
         """
         variable_dict = {}
         for key, value in variables.items():
             if value != 0:
                 if isinstance(key, str):
-                    variable_dict[Var(key)] = float(value)
+                    raise ValueError("Unsupported argument type")
                 else:
                     variable_dict[key] = float(value)
         self.variables = variable_dict
         self.constant = float(constant)
 
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, type(self)):
+            raise ValueError()
         match = self.variables.keys() == other.variables.keys()
         if match:
             for k, v in self.variables.items():
@@ -73,13 +78,15 @@ def __str__(self) -> str:
         res += " <= " + str(self.constant)
         return res
 
-    def __hash__(self):
+    def __hash__(self) -> int:
         return hash(str(self))
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return "<Term {0}>".format(self)
 
-    def __add__(self, other):
+    def __add__(self, other: object) -> PolyhedralTerm:
+        if not isinstance(other, type(self)):
+            raise ValueError()
         varlist = list_union(self.vars, other.vars)
         variables = {}
         for var in varlist:  # noqa: VNE002
@@ -240,7 +247,7 @@ def get_matching_vars(self, variable_polarity: dict[Var, bool]) -> list[Var]:
                     break
         return variable_list
 
-    def remove_variable(self, var: Var):  # noqa: VNE002
+    def remove_variable(self, var: Var) -> None:  # noqa: VNE002
         """
         Eliminates a variable from a term.
 
@@ -336,7 +343,7 @@ def to_term(expression: sympy.core.expr.Expr) -> PolyhedralTerm:
         Returns:
             PolyhedralTerm corresponding to sympy expression.
         """
-        expression_coefficients = expression.as_coefficients_dict()
+        expression_coefficients: dict = expression.as_coefficients_dict()
         logging.debug(expression_coefficients)
         keys = list(expression_coefficients.keys())
         variable_dict = {}
@@ -428,7 +435,7 @@ def solve_for_variables(context: PolyhedralTermList, vars_to_elim: list[Var]) ->
 class PolyhedralTermList(TermList):  # noqa: WPS338
     """A TermList of PolyhedralTerm instances."""
 
-    def __init__(self, terms: Union[list[PolyhedralTerm], None] = None):
+    def __init__(self, terms: Optional[list[PolyhedralTerm]] = None):
         """
         Constructor for PolyhedralTermList.
 
@@ -473,7 +480,7 @@ def to_str_list(self) -> list[str]:
         str_list = []
         ts = self.terms.copy()
         while ts:
-            s, rest = serializer.internal_pt_to_string(ts)
+            s, rest = serializer.polyhedral_term_list_to_strings(ts)
             str_list.append(s)
             ts = rest
         return str_list
@@ -638,7 +645,7 @@ def elim_vars_by_relaxing(self, context: PolyhedralTermList, vars_to_elim: list)
         termlist.terms = list_diff(termlist.terms, terms_to_elim.terms)
         return termlist
 
-    def simplify(self, context: Union[PolyhedralTermList, None] = None) -> None:
+    def simplify(self, context: Optional[PolyhedralTermList] = None) -> None:
         """
         Remove redundant terms in the PolyhedralTermList using the provided context.
 
@@ -715,7 +722,7 @@ def is_empty(self) -> bool:
         logging.debug("Polytope is \n%s", self_mat)
         return PolyhedralTermList.is_polytope_empty(self_mat, self_cons)
 
-    def _transform(self, context: PolyhedralTermList, vars_to_elim: list, refine: bool):
+    def _transform(self, context: PolyhedralTermList, vars_to_elim: list, refine: bool) -> None:
         logging.debug("Transforming: %s", self)
         logging.debug("Context terms: %s", context)
         logging.debug("Variables to eliminate: %s", vars_to_elim)
@@ -735,7 +742,7 @@ def _transform(self, context: PolyhedralTermList, vars_to_elim: list, refine: bo
         logging.debug("Ending transformation with simplification")
         self.simplify(context)
 
-    def optimize(self, objective: dict[Var, numeric], maximize: bool = True):
+    def optimize(self, objective: dict[Var, numeric], maximize: bool = True) -> Optional[numeric]:
         """
         Optimizes a linear expression in the feasible region of the termlist.
 
@@ -763,12 +770,12 @@ def optimize(self, objective: dict[Var, numeric], maximize: bool = True):
         # 2 : Problem appears to be infeasible.
         # 3 : Problem appears to be unbounded.
         # 4 : Numerical difficulties encountered.
-        if res["status"] in {1, 2, 4}:
-            raise ValueError("Constraints are unfeasible")
-        elif res["status"] == 3:
+        if res["status"] == 3:
             return None
         elif res["status"] == 0:
-            return polarity * res["fun"]
+            fun_val: float = res["fun"]
+            return polarity * fun_val
+        raise ValueError("Constraints are unfeasible")
 
     @staticmethod
     def termlist_to_polytope(
@@ -858,7 +865,7 @@ def polytope_to_termlist(matrix: np.ndarray, vector: np.ndarray, variables: list
 
     @staticmethod
     def reduce_polytope(  # noqa: WPS231
-        a: np.ndarray, b: np.ndarray, a_help: Union[np.ndarray, None] = None, b_help: Union[np.ndarray, None] = None
+        a: np.ndarray, b: np.ndarray, a_help: Optional[np.ndarray] = None, b_help: Optional[np.ndarray] = None
     ) -> Tuple[np.ndarray, np.ndarray]:
         """
         Eliminate redundant constraints from a given polytope.
@@ -940,10 +947,10 @@ def reduce_polytope(  # noqa: WPS231
 
     @staticmethod
     def verify_polytope_containment(  # noqa: WPS231
-        a_l: Union[np.ndarray, None] = None,
-        b_l: Union[np.ndarray, None] = None,
-        a_r: Union[np.ndarray, None] = None,
-        b_r: Union[np.ndarray, None] = None,
+        a_l: Optional[np.ndarray] = None,
+        b_l: Optional[np.ndarray] = None,
+        a_r: Optional[np.ndarray] = None,
+        b_r: Optional[np.ndarray] = None,
     ) -> bool:
         """
         Tell whether a polytope is contained in another.
@@ -1053,7 +1060,7 @@ def is_polytope_empty(a: np.ndarray, b: np.ndarray) -> bool:
     @staticmethod
     def _get_kaykobad_context(  # noqa: WPS231
         term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool
-    ):
+    ) -> Tuple[list[PolyhedralTerm], list[Var]]:
         forbidden_vars = list_intersection(vars_to_elim, term.vars)
         other_forbibben_vars = list_diff(vars_to_elim, term.vars)
         n = len(forbidden_vars)
@@ -1120,13 +1127,14 @@ def _get_kaykobad_context(  # noqa: WPS231
             if not row_found:
                 raise ValueError("Could not find the {}th row of matrix".format(i))
         if (not matrix_contains_others) and len(list_diff(term.vars, vars_to_elim)) == 0:
-            logging.debug("Hola2")
             raise ValueError("Found context will produce empty transformation")
         logging.debug("Matrix row terms %s", matrix_row_terms)
         return matrix_row_terms, forbidden_vars
 
     @staticmethod
-    def _tactic_1(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool):
+    def _tactic_1(
+        term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool
+    ) -> PolyhedralTerm:
         logging.debug("********** Tactic 1")
         logging.debug("Vars_to_elim %s \nTerm %s \nContext %s " % (vars_to_elim, term, context))
         try:
@@ -1136,8 +1144,8 @@ def _tactic_1(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: l
         except ValueError:
             logging.debug("Could not transform %s using Tactic 1", term)
             raise ValueError("Could not transform term {}".format(term))
-        matrix_row_terms = PolyhedralTermList(list(matrix_row_terms))
-        sols = PolyhedralTerm.solve_for_variables(matrix_row_terms, list(forbidden_vars))
+        matrix_row_terms_tl = PolyhedralTermList(list(matrix_row_terms))
+        sols = PolyhedralTerm.solve_for_variables(matrix_row_terms_tl, list(forbidden_vars))
         logging.debug("Sols %s", sols)
 
         result = term.copy()
@@ -1149,7 +1157,9 @@ def _tactic_1(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: l
         return result
 
     @staticmethod
-    def _tactic_2(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool):  # noqa: WPS231
+    def _tactic_2(  # noqa: WPS231
+        term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool
+    ) -> PolyhedralTerm:
         logging.debug("************ Tactic 2")
         new_context_list = []
         logging.debug("This is the context")
@@ -1192,7 +1202,9 @@ def _tactic_2(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: l
         return result
 
     @staticmethod
-    def _tactic_3(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool):
+    def _tactic_3(
+        term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool
+    ) -> PolyhedralTerm:
         logging.debug("************ Tactic 3")
         conflict_vars = list_intersection(vars_to_elim, term.vars)
         conflict_coeff = {var: term.get_coefficient(var) for var in conflict_vars}
@@ -1216,7 +1228,9 @@ def _tactic_3(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: l
         return result
 
     @staticmethod
-    def _transform_term(term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool):
+    def _transform_term(
+        term: PolyhedralTerm, context: PolyhedralTermList, vars_to_elim: list, refine: bool
+    ) -> PolyhedralTerm:
         logging.debug("Transforming term: %s", term)
         logging.debug("Context: %s", context)
 
diff --git a/src/pacti/terms/polyhedra/polyhedral_contract.py b/src/pacti/terms/polyhedra/polyhedral_contract.py
index 94f3ca2b..d9b39774 100644
--- a/src/pacti/terms/polyhedra/polyhedral_contract.py
+++ b/src/pacti/terms/polyhedra/polyhedral_contract.py
@@ -2,13 +2,18 @@
 
 from __future__ import annotations
 
-from typing import Optional, Tuple, Union
+from typing import Optional, Tuple, TypedDict, Union
 
 from pacti.iocontract import IoContract, IoContractCompound, NestedTermList, Var
 from pacti.terms.polyhedra import serializer
 from pacti.terms.polyhedra.polyhedra import PolyhedralTerm, PolyhedralTermList
 
 numeric = Union[int, float]
+ser_pt = dict[str, Union[float, dict[str, float]]]
+ser_contract = TypedDict(
+    "ser_contract",
+    {"input_vars": list[str], "output_vars": list[str], "assumptions": list[ser_pt], "guarantees": list[ser_pt]},
+)
 
 
 class PolyhedralContract(IoContract):
@@ -29,35 +34,38 @@ def rename_variables(self, variable_mappings: list[tuple[str, str]]) -> Polyhedr
             new_contract = new_contract.rename_variable(Var(mapping[0]), Var(mapping[1]))
         return new_contract
 
-    def to_machine_dict(self):
+    def to_machine_dict(self) -> ser_contract:
         """
         Map contract into a machine-optimized dictionary.
 
         Returns:
             A dictionary containing the contract's information.
         """
-        c_temp = {}
-        c_temp["input_vars"] = [str(x) for x in self.inputvars]
-        c_temp["output_vars"] = [str(x) for x in self.outputvars]
-
-        c_temp["assumptions"] = [
+        input_vars = [str(x) for x in self.inputvars]
+        output_vars = [str(x) for x in self.outputvars]
+        assumptions: list[ser_pt] = [
             {
                 "constant": float(term.constant),
                 "coefficients": {str(k): float(v) for k, v in term.variables.items()},
             }
             for term in self.a.terms
         ]
-
-        c_temp["guarantees"] = [
+        guarantees: list[ser_pt] = [
             {
                 "constant": float(term.constant),
                 "coefficients": {str(k): float(v) for k, v in term.variables.items()},
             }
             for term in self.g.terms
         ]
-        return c_temp
 
-    def to_dict(self):
+        return {
+            "input_vars": input_vars,
+            "output_vars": output_vars,
+            "assumptions": assumptions,
+            "guarantees": guarantees,
+        }
+
+    def to_dict(self) -> dict:
         """
         Map contract into a user-readable dictionary.
 
@@ -92,11 +100,11 @@ def from_string(
         """
         a: list[PolyhedralTerm] = []
         if assumptions:
-            a = [item for x in assumptions for item in serializer.internal_pt_from_string(x)]
+            a = [item for x in assumptions for item in serializer.polyhedral_termlist_from_string(x)]
 
         g: list[PolyhedralTerm] = []
         if guarantees:
-            g = [item for x in guarantees for item in serializer.internal_pt_from_string(x)]
+            g = [item for x in guarantees for item in serializer.polyhedral_termlist_from_string(x)]
 
         return PolyhedralContract(
             input_vars=[Var(x) for x in input_vars],
@@ -121,17 +129,26 @@ def from_dict(contract: dict) -> PolyhedralContract:
         """
         if not isinstance(contract, dict):
             raise ValueError("A dict type contract is expected.")
+        for kw in ("assumptions", "guarantees", "input_vars", "output_vars"):
+            if kw not in contract:
+                raise ValueError(f"Passed dictionary does not have key {kw}.")
 
         if all(isinstance(x, dict) for x in contract["assumptions"]):
             a = PolyhedralTermList(
-                [PolyhedralTerm(x["coefficients"], float(x["constant"])) for x in contract["assumptions"]]
+                [
+                    PolyhedralTerm({Var(k): v for k, v in x["coefficients"].items()}, float(x["constant"]))
+                    for x in contract["assumptions"]
+                ]
             )
         else:
             raise ValueError("Assumptions must be a list of dicts.")
 
         if all(isinstance(x, dict) for x in contract["guarantees"]):
             g = PolyhedralTermList(
-                [PolyhedralTerm(x["coefficients"], float(x["constant"])) for x in contract["guarantees"]]
+                [
+                    PolyhedralTerm({Var(k): v for k, v in x["coefficients"].items()}, float(x["constant"]))
+                    for x in contract["guarantees"]
+                ]
             )
         else:
             raise ValueError("Guarantees must be a list of dicts.")
@@ -143,7 +160,7 @@ def from_dict(contract: dict) -> PolyhedralContract:
             guarantees=g,
         )
 
-    def compose(self, other: PolyhedralContract, vars_to_keep: Optional[list[str]] = None):
+    def compose(self, other: PolyhedralContract, vars_to_keep: Optional[list[str]] = None) -> PolyhedralContract:
         """Compose polyhedral contracts.
 
         Compute the composition of the two given contracts and abstract the
@@ -164,7 +181,7 @@ def compose(self, other: PolyhedralContract, vars_to_keep: Optional[list[str]] =
             vars_to_keep = []
         return super().compose(other, [Var(x) for x in vars_to_keep])
 
-    def optimize(self, expr: str, maximize: bool = True):
+    def optimize(self, expr: str, maximize: bool = True) -> Optional[numeric]:
         """Optimize linear objective over the contract.
 
         Compute the optima of a linear objective over the assumptions and
@@ -180,7 +197,7 @@ def optimize(self, expr: str, maximize: bool = True):
             The optimal value of the objective in the context of the contract.
         """
         new_expr = expr + " <= 0"
-        variables = serializer.internal_pt_from_string(new_expr)[0].variables
+        variables = serializer.polyhedral_termlist_from_string(new_expr)[0].variables
         constraints: PolyhedralTermList = self.a | self.g
         return constraints.optimize(objective=variables, maximize=maximize)
 
@@ -255,13 +272,13 @@ def from_string(
         a: list[PolyhedralTermList] = []
         if assumptions:
             for termlist_str in assumptions:
-                a_termlist = [item for x in termlist_str for item in serializer.internal_pt_from_string(x)]
+                a_termlist = [item for x in termlist_str for item in serializer.polyhedral_termlist_from_string(x)]
                 a.append(PolyhedralTermList(a_termlist))
 
         g: list[PolyhedralTermList] = []
         if guarantees:
             for termlist_str in guarantees:
-                g_termlist = [item for x in termlist_str for item in serializer.internal_pt_from_string(x)]
+                g_termlist = [item for x in termlist_str for item in serializer.polyhedral_termlist_from_string(x)]
                 g.append(PolyhedralTermList(g_termlist))
 
         return PolyhedralContractCompound(
diff --git a/src/pacti/terms/polyhedra/serializer.py b/src/pacti/terms/polyhedra/serializer.py
index 23ab6ba9..5dcaff11 100644
--- a/src/pacti/terms/polyhedra/serializer.py
+++ b/src/pacti/terms/polyhedra/serializer.py
@@ -1,23 +1,31 @@
-"""Consists of loader functions that can read a JSON dictionary contract or write a IOContract to a JSON file."""
+"""Transformations between polyhedral structures and strings."""
 import re
-from typing import Tuple, Union
+from typing import Dict, Tuple, Union
 
 import numpy as np
 import sympy
-from typing_extensions import TypedDict
 
+from pacti.iocontract import Var
 from pacti.terms.polyhedra.polyhedra import PolyhedralTerm
 from pacti.utils.errors import ContractFormatError
 
 numeric = Union[int, float]
-ser_pt = dict[str, Union[float, dict[str, float]]]
-ser_contract = TypedDict(
-    "ser_contract",
-    {"input_vars": list[str], "output_vars": list[str], "assumptions": list[ser_pt], "guarantees": list[ser_pt]},
-)
 
 
-def validate_contract_dict(contract, contract_name, machine_representation: bool):
+def validate_contract_dict(  # noqa: WPS231 too much cognitive complexity
+    contract: Dict, contract_name: str, machine_representation: bool
+) -> None:
+    """
+    Tell whether a contract dictionary can be read as a polyhedral contract.
+
+    Args:
+        contract: a dictionary to be analyzed.
+        contract_name: a name for the contract (used for error reporting).
+        machine_representation: the provided dictionary is machine-optimized.
+
+    Raises:
+        ContractFormatError: the provided contract is not well-formed.
+    """
     if not isinstance(contract, dict):
         print(contract)
         raise ContractFormatError("Each contract should be a dictionary")
@@ -37,10 +45,10 @@ def validate_contract_dict(contract, contract_name, machine_representation: bool
                     raise ContractFormatError(f"The {kw} in contract {contract_name} should be defined as strings")
         elif machine_representation:
             for index, clause in enumerate(value):
-                check_clause(clause, f"{contract_name}:{kw}{index}")
+                _check_clause(clause, f"{contract_name}:{kw}{index}")
 
 
-def check_clause(clause, clause_id):
+def _check_clause(clause: dict, clause_id: str) -> None:
     keywords = ["constant", "coefficients"]
     for kw in keywords:
         if kw not in clause:
@@ -55,14 +63,14 @@ def check_clause(clause, clause_id):
 float_closeness_absolute_tolerance: float = 1e-8
 
 
-def number2string(n: numeric) -> str:
+def _number_to_string(n: numeric) -> str:
     if isinstance(n, sympy.core.numbers.Float):
         f: sympy.core.numbers.Float = n
         return str(f.num)
     return str(n)
 
 
-def are_numbers_approximatively_equal(v1: numeric, v2: numeric) -> bool:
+def _are_numbers_approximatively_equal(v1: numeric, v2: numeric) -> bool:
     if isinstance(v1, int) & isinstance(v2, int):
         return v1 == v2
     f1 = float(v1)
@@ -74,7 +82,7 @@ def are_numbers_approximatively_equal(v1: numeric, v2: numeric) -> bool:
     )
 
 
-def _lhs_str(term) -> str:  # noqa: WPS231
+def _lhs_str(term: PolyhedralTerm) -> str:  # noqa: WPS231
     varlist = list(term.variables.items())
     varlist.sort(key=lambda x: str(x[0]))
     # res = " + ".join([str(coeff) + "*" + var.name for var, coeff in varlist])
@@ -82,33 +90,44 @@ def _lhs_str(term) -> str:  # noqa: WPS231
     res = ""
     first = True
     for var, coeff in varlist:  # noqa: VNE002
-        if are_numbers_approximatively_equal(coeff, 1.0):
+        if _are_numbers_approximatively_equal(coeff, 1.0):
             if first:
                 res += var.name
             else:
                 res += " + " + var.name
-        elif are_numbers_approximatively_equal(coeff, -1.0):
+        elif _are_numbers_approximatively_equal(coeff, -1.0):
             if first:
                 res += "-" + var.name
             else:
                 res += " - " + var.name
-        elif not are_numbers_approximatively_equal(coeff, float(0)):
+        elif not _are_numbers_approximatively_equal(coeff, float(0)):
             if coeff > 0:
                 if first:
-                    res += number2string(coeff) + " " + var.name
+                    res += _number_to_string(coeff) + " " + var.name
                 else:
-                    res += " + " + number2string(coeff) + " " + var.name
+                    res += " + " + _number_to_string(coeff) + " " + var.name
             else:
                 if first:
-                    res += number2string(coeff) + " " + var.name
+                    res += _number_to_string(coeff) + " " + var.name
                 else:
-                    res += " - " + number2string(-coeff) + " " + var.name
+                    res += " - " + _number_to_string(-coeff) + " " + var.name
         first = False
-    # res += " <= " + number2string(self.constant)
+    # res += " <= " + _number_to_string(self.constant)
     return res
 
 
-def internal_pt_to_string(terms: list[PolyhedralTerm]) -> Tuple[str, list[PolyhedralTerm]]:
+def polyhedral_term_list_to_strings(  # noqa: WPS231 too much cognitive complexity
+    terms: list[PolyhedralTerm],
+) -> Tuple[str, list[PolyhedralTerm]]:
+    """
+    Convert a list of polyhedral terms into a list of strings, one term at a time.
+
+    Args:
+        terms: the list of terms.
+
+    Returns:
+        String representation of the first constraint and list of items not yet serialized.
+    """
     if not terms:
         return "", []
 
@@ -116,22 +135,23 @@ def internal_pt_to_string(terms: list[PolyhedralTerm]) -> Tuple[str, list[Polyhe
 
     ts = terms[1:]
     for tn in ts:
-        if are_polyhedral_terms_opposite(tp, tn):
+        if _are_polyhedral_terms_opposite(tp, tn):
             # tp has the form: LHS
             # tn has the form: -(LHS)
-            if are_numbers_approximatively_equal(tp.constant, -tn.constant):
+            if _are_numbers_approximatively_equal(tp.constant, -tn.constant):
                 # inverse of rule 4
                 # rewrite as 2 terms given input match: LHS = RHS
                 # pos: LHS <= RHS
                 # neg: -(LHS) <= -(RHS)
-                s = _lhs_str(tp) + " = " + number2string(tp.constant)
+                s = _lhs_str(tp) + " = " + _number_to_string(tp.constant)
                 ts.remove(tn)
                 return s, ts
 
             else:
-                if are_numbers_approximatively_equal(tp.constant, float(0)) & are_numbers_approximatively_equal(
-                    tn.constant, float(0)
-                ):
+                condition = _are_numbers_approximatively_equal(
+                    tp.constant, float(0)
+                ) and _are_numbers_approximatively_equal(tn.constant, float(0))
+                if condition:
                     # inverse of rule 3
                     # rewrite as 2 terms given input match: | LHS | = 0
                     # pos: LHS <= 0
@@ -139,16 +159,16 @@ def internal_pt_to_string(terms: list[PolyhedralTerm]) -> Tuple[str, list[Polyhe
                     s = "|" + _lhs_str(tp) + "| = 0"
                     ts.remove(tn)
                     return s, ts
-                elif are_numbers_approximatively_equal(tp.constant, tn.constant):
+                elif _are_numbers_approximatively_equal(tp.constant, tn.constant):
                     # inverse of rule 2
                     # rewrite as 2 terms given input match: | LHS | <= RHS
                     # pos: LHS <= RHS
                     # neg: -(LHS) <= RHS
-                    s = "|" + _lhs_str(tp) + "| <= " + number2string(tp.constant)
+                    s = "|" + _lhs_str(tp) + "| <= " + _number_to_string(tp.constant)
                     ts.remove(tn)
                     return s, ts
 
-    s = _lhs_str(tp) + " <= " + number2string(tp.constant)
+    s = _lhs_str(tp) + " <= " + _number_to_string(tp.constant)
     return s, ts
 
 
@@ -184,20 +204,22 @@ def internal_pt_to_string(terms: list[PolyhedralTerm]) -> Tuple[str, list[Polyhe
     "$"
 )
 
-internal_polyhedral_term_absolute_less_than_pattern = re.compile(
+internal_polyhedral_term_absolute_less_than_pattern = re.compile(  # noqa: WPS118 Found too long name
     "^"
     r"\s*\|"
-    r"(?P<LHS>([+-]?(\s*(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)?)?\s*\*?\s*[a-zA-Z]\w*(\s*[+-]\s*((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*\*?)?\s*[a-zA-Z]\w*)*)"
+    r"(?P<LHS>([+-]?(\s*(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)?)?\s*\*?\s*"
+    r"[a-zA-Z]\w*(\s*[+-]\s*((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*\*?)?\s*[a-zA-Z]\w*)*)"
     r"\s*\|"
     r"\s*<="
     r"\s*(?P<RHS>[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)"
     "$"
 )
 
-internal_polyhedral_term_absolute_zero_pattern = re.compile(
+internal_polyhedral_term_absolute_zero_pattern = re.compile(  # noqa: WPS118 Found too long name
     "^"
     r"\s*\|"
-    r"(?P<LHS>([+-]?(\s*(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)?)?\s*\*?\s*[a-zA-Z]\w*(\s*[+-]\s*((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*\*?)?\s*[a-zA-Z]\w*)*)"
+    r"(?P<LHS>([+-]?(\s*(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)?)?\s*\*?\s*"
+    r"[a-zA-Z]\w*(\s*[+-]\s*((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*\*?)?\s*[a-zA-Z]\w*)*)"
     r"\s*\|"
     r"\s*="
     r"\s*0"
@@ -206,7 +228,8 @@ def internal_pt_to_string(terms: list[PolyhedralTerm]) -> Tuple[str, list[Polyhe
 
 internal_polyhedral_term_equality_pattern = re.compile(
     "^"
-    r"(?P<LHS>([+-]?(\s*(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)?)?\s*\*?\s*[a-zA-Z]\w*(\s*[+-]\s*((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*\*?)?\s*[a-zA-Z]\w*)*)"
+    r"(?P<LHS>([+-]?(\s*(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)?)?\s*\*?\s*"
+    r"[a-zA-Z]\w*(\s*[+-]\s*((\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*\*?)?\s*[a-zA-Z]\w*)*)"
     r"\s*="
     r"\s*(?P<RHS>[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)"
     "$"
@@ -216,7 +239,7 @@ def internal_pt_to_string(terms: list[PolyhedralTerm]) -> Tuple[str, list[Polyhe
 # opposite terms means:
 # - same set of variables
 # - the variable coefficients of self are approximatively the negative of those of other.
-def are_polyhedral_terms_opposite(self: PolyhedralTerm, other: PolyhedralTerm) -> bool:
+def _are_polyhedral_terms_opposite(self: PolyhedralTerm, other: PolyhedralTerm) -> bool:
     for var in other.variables.keys():
         if not self.contains_var(var):
             return False
@@ -224,12 +247,12 @@ def are_polyhedral_terms_opposite(self: PolyhedralTerm, other: PolyhedralTerm) -
     for var, value in self.variables.items():
         if not other.contains_var(var):
             return False
-        if not are_numbers_approximatively_equal(-value, other.variables[var]):
+        if not _are_numbers_approximatively_equal(-value, other.variables[var]):
             return False
     return True
 
 
-def internal_parse_constant(val: str) -> numeric:
+def _internal_parse_constant(val: str) -> numeric:
     if val == "":
         return 1.0
     elif internal_plus_pattern.match(val):
@@ -248,56 +271,56 @@ def internal_parse_constant(val: str) -> numeric:
     return n
 
 
-def internal_add_variable(terms: str, variables: dict[str, numeric], v: str, c: str) -> None:
+def _internal_add_variable(terms: str, variables: dict[str, numeric], v: str, c: str) -> None:
     if v in variables:
         raise (ValueError(f"Multiple coefficients involving the same variable: {v} in: {terms}"))
 
-    n = internal_parse_constant(c)
+    n = _internal_parse_constant(c)
     variables.update({v: n})
 
 
-def internal_parse_variables(variables: dict[str, numeric], terms: str) -> None:
+def _internal_parse_variables(variables: dict[str, numeric], terms: str) -> None:
     t = internal_variable_pattern.match(terms)
     if not t:
         raise (ValueError(f"Polyhedral variable syntax mismatch: {terms}"))
 
     v = t.group("variable")
     c = t.group("coefficient")
-    internal_add_variable(terms, variables, v, c)
+    _internal_add_variable(terms, variables, v, c)
 
     rest = t.group("variables")
     if rest:
-        internal_parse_variables(variables, rest)
+        _internal_parse_variables(variables, rest)
     else:
         variables
 
 
-def internal_pt_from_canonical_match(m: re.Match[str]) -> PolyhedralTerm:
+def _internal_pt_from_canonical_match(m: re.Match[str]) -> PolyhedralTerm:
     variables: dict[str, numeric] = {}
 
     v = m.group("variable")
     c = m.group("coefficient")
-    internal_add_variable(m.group(0), variables, v, c)
+    _internal_add_variable(m.group(0), variables, v, c)
 
     rest = m.group("variables")
     if rest:
-        internal_parse_variables(variables, rest)
+        _internal_parse_variables(variables, rest)
 
     constant = float(m.group("constant"))
-    return PolyhedralTerm(variables, constant)
+    return PolyhedralTerm({Var(k): v for k, v in variables.items()}, constant)
 
 
 # rewrite as 2 terms given input match: | LHS | <= RHS
 # pos: LHS <= RHS
 # neg: -(LHS) <= RHS
 # result is [pos,neg]
-def internal_pt_from_absolute_less_than_match(m: re.Match[str]) -> list[PolyhedralTerm]:
+def _internal_pt_from_absolute_less_than_match(m: re.Match[str]) -> list[PolyhedralTerm]:
     s1 = f"{m.group('LHS')} <= {m.group('RHS')}"
     m1 = internal_polyhedral_term_canonical_pattern.match(s1)
     if not m1:
         raise ValueError(f"Invalid 'LHS <= RHS' syntax in: {s1}")
 
-    pos: PolyhedralTerm = internal_pt_from_canonical_match(m1)
+    pos: PolyhedralTerm = _internal_pt_from_canonical_match(m1)
     neg: PolyhedralTerm = pos.copy()
     for key, value in neg.variables.items():
         neg.variables.update({key: -value})
@@ -308,13 +331,13 @@ def internal_pt_from_absolute_less_than_match(m: re.Match[str]) -> list[Polyhedr
 # pos: LHS <= 0
 # neg: -(LHS) <= 0
 # result is [pos,neg]
-def internal_pt_from_absolute_zero_match(m: re.Match[str]) -> list[PolyhedralTerm]:
+def _internal_pt_from_absolute_zero_match(m: re.Match[str]) -> list[PolyhedralTerm]:
     s1 = f"{m.group('LHS')} <= 0"
     m1 = internal_polyhedral_term_canonical_pattern.match(s1)
     if not m1:
         raise ValueError(f"Invalid 'LHS <= 0' syntax in: {s1}")
 
-    pos: PolyhedralTerm = internal_pt_from_canonical_match(m1)
+    pos: PolyhedralTerm = _internal_pt_from_canonical_match(m1)
     neg: PolyhedralTerm = pos.copy()
     for key, value in neg.variables.items():
         neg.variables.update({key: -value})
@@ -325,13 +348,13 @@ def internal_pt_from_absolute_zero_match(m: re.Match[str]) -> list[PolyhedralTer
 # pos: LHS <= RHS
 # neg: -(LHS) <= -(RHS)
 # result is [pos,neg]
-def internal_pt_from_equality_match(m: re.Match[str]) -> list[PolyhedralTerm]:
+def _internal_pt_from_equality_match(m: re.Match[str]) -> list[PolyhedralTerm]:
     s1 = f"{m.group('LHS')} <= {m.group('RHS')}"
     m1 = internal_polyhedral_term_canonical_pattern.match(s1)
     if not m1:
         raise ValueError(f"Invalid 'LHS <= RHS' syntax in: {s1}")
 
-    pos: PolyhedralTerm = internal_pt_from_canonical_match(m1)
+    pos: PolyhedralTerm = _internal_pt_from_canonical_match(m1)
     neg: PolyhedralTerm = pos.copy()
     for key, value in neg.variables.items():
         neg.variables.update({key: -value})
@@ -339,21 +362,33 @@ def internal_pt_from_equality_match(m: re.Match[str]) -> list[PolyhedralTerm]:
     return [pos, neg]
 
 
-def internal_pt_from_string(str_rep: str) -> list[PolyhedralTerm]:
+def polyhedral_termlist_from_string(str_rep: str) -> list[PolyhedralTerm]:
+    """
+    Transform a linear expression into a polyhedral termlist.
+
+    Args:
+        str_rep: The linear expression passed as a string.
+
+    Returns:
+        A PolyhedralTermList representing the input expression.
+
+    Raises:
+        ValueError: constraint syntax invalid.
+    """
     m1 = internal_polyhedral_term_canonical_pattern.match(str_rep)
     m2 = internal_polyhedral_term_absolute_less_than_pattern.match(str_rep)
     m3 = internal_polyhedral_term_absolute_zero_pattern.match(str_rep)
     m4 = internal_polyhedral_term_equality_pattern.match(str_rep)
     if m1:
-        return [internal_pt_from_canonical_match(m1)]
+        return [_internal_pt_from_canonical_match(m1)]
 
     elif m2:
-        return internal_pt_from_absolute_less_than_match(m2)
+        return _internal_pt_from_absolute_less_than_match(m2)
 
     elif m3:
-        return internal_pt_from_absolute_zero_match(m3)
+        return _internal_pt_from_absolute_zero_match(m3)
 
     elif m4:
-        return internal_pt_from_equality_match(m4)
+        return _internal_pt_from_equality_match(m4)
 
     raise ValueError(f"Polyhedral term syntax mismatch: {str_rep}")
diff --git a/src/pacti/utils/fileio.py b/src/pacti/utils/fileio.py
index 0786f97f..4a2b2511 100644
--- a/src/pacti/utils/fileio.py
+++ b/src/pacti/utils/fileio.py
@@ -2,7 +2,7 @@
 
 import json
 import os
-from typing import Tuple
+from typing import Any, Tuple
 
 from pacti.terms import polyhedra
 
@@ -67,7 +67,7 @@ def write_contracts_to_file(
     data = []
     assert len(contracts) == len(names)
     for i, c in enumerate(contracts):
-        entry = {}
+        entry: dict[str, Any] = {}
         if isinstance(c, polyhedra.PolyhedralContract):
             entry["name"] = names[i]
             if machine_representation:
diff --git a/src/pacti/utils/lists.py b/src/pacti/utils/lists.py
index 8274e5a7..9340d4f5 100644
--- a/src/pacti/utils/lists.py
+++ b/src/pacti/utils/lists.py
@@ -1,7 +1,9 @@
 """Some list operations."""
 
+from typing import Any
 
-def list_intersection(list1: list, list2: list) -> list:
+
+def list_intersection(list1: list[Any], list2: list[Any]) -> list[Any]:
     """
     Intersect two lists.
 
@@ -15,7 +17,7 @@ def list_intersection(list1: list, list2: list) -> list:
     return [el for el in list1 if el in list2]
 
 
-def list_diff(list1: list, list2: list) -> list:
+def list_diff(list1: list[Any], list2: list[Any]) -> list[Any]:
     """
     Compute difference between lists.
 
@@ -29,7 +31,7 @@ def list_diff(list1: list, list2: list) -> list:
     return [el for el in list1 if (el not in list2)]
 
 
-def list_union(list1: list, list2: list) -> list:
+def list_union(list1: list[Any], list2: list[Any]) -> list[Any]:
     """
     Compute the union of two lists.
 
@@ -43,7 +45,7 @@ def list_union(list1: list, list2: list) -> list:
     return list1 + [el for el in list2 if (el not in list1)]
 
 
-def lists_equal(list1: list, list2: list) -> bool:
+def lists_equal(list1: list[Any], list2: list[Any]) -> bool:
     """
     Tells whether two lists have the same elements.
 
diff --git a/src/pacti/utils/multiple_composition.py b/src/pacti/utils/multiple_composition.py
index 2d4af21d..d516ed58 100644
--- a/src/pacti/utils/multiple_composition.py
+++ b/src/pacti/utils/multiple_composition.py
@@ -1,27 +1,29 @@
+"""Support for multiple composition."""
 from __future__ import annotations
 
+from typing import Any, Dict
+
 from pacti.iocontract import IoContract
-from pacti.terms.polyhedra import PolyhedralContract
 
 
-class Tree:
-    def __init__(self, data=None):
+class _Tree:
+    def __init__(self, data: Any = None):
         self.data = data
-        self.children = set()
+        self.children: set[Any] = set()
 
-    def add_child(self, child: Tree):
+    def add_child(self, child: _Tree) -> None:
         self.children.add(child)
 
-    def get_leafs(self) -> set[Tree]:
+    def get_leafs(self) -> set[_Tree]:
         leafs = set()
-        if not self.children:
-            leafs.add(self)
-        else:
+        if self.children:
             for child in self.children:
                 leafs.update(child.get_leafs())
+        else:
+            leafs.add(self)
         return leafs
 
-    def remove_node(self, leaf):
+    def remove_node(self, leaf: Any) -> None:
         if not self.children:
             return
         if leaf in self.children:
@@ -30,38 +32,47 @@ def remove_node(self, leaf):
         for child in self.children:
             child.remove_node(leaf)
 
-    def print_tree(self, level=0, is_last_sibling=True):
+    def print_tree(self, level: int = 0, is_last_sibling: bool = True) -> None:
         prefix = "    " * level
-        if not is_last_sibling:
-            prefix += "|-- "
-        else:
+        if is_last_sibling:
             prefix += "`-- "
+        else:
+            prefix += "|-- "
         print(prefix + str(self.data))
         for i, child in enumerate(self.children):
             is_last_child = i == len(self.children) - 1
             child.print_tree(level + 1, is_last_child)
 
 
-def build_dependency_tree(symbol_dict):
-    root = Tree()
+def _build_dependency_tree(symbol_dict: Dict) -> _Tree:  # noqa: WPS231 too much cognitive complexity
+    root = _Tree()
     symbol_nodes = {}
     for symbol, value in symbol_dict.items():
         inputs, outputs = value
         if symbol not in symbol_nodes:
-            symbol_nodes[symbol] = Tree(symbol)
+            symbol_nodes[symbol] = _Tree(symbol)
             root.add_child(symbol_nodes[symbol])
         for output in outputs:
             for dependency, dep_values in symbol_dict.items():
                 if output in dep_values[0]:
                     if dependency not in symbol_nodes:
-                        symbol_nodes[dependency] = Tree(dependency)
+                        symbol_nodes[dependency] = _Tree(dependency)
                         # root.add_child(symbol_nodes[dependency])
                     symbol_nodes[symbol].add_child(symbol_nodes[dependency])
     return root
 
 
-def composing_multiple_contracts(contracts: list[IoContract]) -> IoContract:
-    symbol_dict: dict[str, tuple[list[str], list[str]]] = {}
+def compose_multiple_contracts(contracts: list[IoContract]) -> IoContract:
+    """
+    Compose several contracts, finding the right order to compose them.
+
+    Args:
+        contracts: list of contracts to be composed.
+
+    Returns:
+        A contract corresponding to the ordered composition of the given contracts.
+    """
+    symbol_dict: dict[str, tuple[list[str], list[str]]] = {}  # noqa: WPS234 complex annotation
     contracts_dict = {}
     for i, contract in enumerate(contracts):
         input_symbols = [v.name for v in contract.inputvars]
@@ -69,39 +80,15 @@ def composing_multiple_contracts(contracts: list[IoContract]) -> IoContract:
         symbol_dict[f"c{i}"] = (input_symbols, output_symbols)
         contracts_dict[f"c{i}"] = contract
 
-    dependency_tree: Tree = build_dependency_tree(symbol_dict)
+    dependency_tree: _Tree = _build_dependency_tree(symbol_dict)
     leaf = dependency_tree.get_leafs().pop()
     main_contract = contracts_dict[leaf.data]
-    dependency_tree.print_tree()
     dependency_tree.remove_node(leaf)
-    dependency_tree.print_tree()
     while dependency_tree is not None:
         leaf = dependency_tree.get_leafs().pop()
-        print(main_contract)
-        print("composing with")
-        print(contracts_dict[leaf.data])
+        if leaf.data is None:
+            break
         main_contract.compose(contracts_dict[leaf.data])
         dependency_tree.remove_node(leaf)
 
     return main_contract
-
-
-if __name__ == "__main__":
-    c1 = PolyhedralContract.from_string(
-        assumptions=["x <= 1"], guarantees=["y <= 0"], input_vars=["x"], output_vars=["y"]
-    )
-    c2 = PolyhedralContract.from_string(
-        assumptions=["y <= 0"], guarantees=["z <= 0"], input_vars=["y"], output_vars=["z"]
-    )
-    c3 = PolyhedralContract.from_string(
-        assumptions=["y <= 0"], guarantees=["q <= 0"], input_vars=["y"], output_vars=["q"]
-    )
-    c4 = PolyhedralContract.from_string(
-        assumptions=["q <= 0"], guarantees=["v <= 0"], input_vars=["q"], output_vars=["v"]
-    )
-    c5 = PolyhedralContract.from_string(
-        assumptions=["y <= 0"], guarantees=["v <= 0"], input_vars=["y"], output_vars=["v"]
-    )
-
-    contract = composing_multiple_contracts([c1, c2, c3, c4, c5])
-    print(contract)


Test patch:
diff --git a/tests/test_data/polyhedral_contracts/test_composition_failure_dspexample1failed.json b/tests/test_data/polyhedral_contracts/test_composition_failure_dspexample1failed.json
new file mode 100644
index 00000000..b54ab3cd
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_failure_dspexample1failed.json
@@ -0,0 +1,45 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p2_a",
+        "p2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p2_a <= 7.75",
+        "-p2_a <= 0.0",
+        "p2_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p2_a",
+        "p2_e",
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25",
+        "p2_a <= 4.125"
+      ],
+      "guarantees": [
+        "-p2_a - p4_a + p5_a <= 3.875",
+        "-p2_e - p4_e + p5_e <= 0.1875",
+        "-p4_a + p5_a <= 7.75",
+        "p5_a <= 7.875"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition1.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition1.json
new file mode 100644
index 00000000..f6165cdd
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition1.json
@@ -0,0 +1,78 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p1_a",
+        "p1_e",
+        "p2_a",
+        "p2_e"
+      ],
+      "output_vars": [
+        "p3_a",
+        "p3_e"
+      ],
+      "assumptions": [
+        "p1_a + p2_a <= 8.0"
+      ],
+      "guarantees": [
+        "-p1_e - p2_e + p3_e <= 0.125",
+        "p3_a <= 7.75",
+        "-p1_a - p2_a + p3_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p3_a",
+        "p3_e",
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p3_a + p4_a <= 8.0"
+      ],
+      "guarantees": [
+        "-p3_e - p4_e + p5_e <= 0.0625",
+        "p5_a <= 7.875",
+        "-p3_a - p4_a + p5_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contractsys",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p1_a",
+        "p1_e",
+        "p2_a",
+        "p2_e",
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25",
+        "p1_a + p2_a <= 8.0"
+      ],
+      "guarantees": [
+        "-p1_e - p2_e - p4_e + p5_e <= 0.1875",
+        "-p4_a + p5_a <= 7.75",
+        "-p1_a - p2_a - p4_a + p5_a <= 0.0",
+        "p5_a <= 7.875"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition2.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition2.json
new file mode 100644
index 00000000..b79a7e66
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition2.json
@@ -0,0 +1,73 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p1_a",
+        "p1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p1_a <= 2.0",
+        "-p1_a <= 0.0",
+        "p1_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p1_a",
+        "p1_e",
+        "p2_a",
+        "p2_e",
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25",
+        "p1_a + p2_a <= 8.0"
+      ],
+      "guarantees": [
+        "-p1_e - p2_e - p4_e + p5_e <= 0.1875",
+        "-p4_a + p5_a <= 7.75",
+        "-p1_a - p2_a - p4_a + p5_a <= 0.0",
+        "p5_a <= 7.875"
+      ]
+    }
+  },
+  {
+    "name": "contractsys",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p2_a",
+        "p2_e",
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25",
+        "p2_a <= 6.0"
+      ],
+      "guarantees": [
+        "-p2_a - p4_a + p5_a <= 2.0",
+        "-p2_e - p4_e + p5_e <= 0.1875",
+        "-p4_a + p5_a <= 7.75",
+        "p5_a <= 7.875"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition3.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition3.json
new file mode 100644
index 00000000..047cd557
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition3.json
@@ -0,0 +1,66 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p2_a",
+        "p2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p2_a <= 3.75",
+        "-p2_a <= 0.0",
+        "p2_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p2_a",
+        "p2_e",
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25",
+        "p2_a <= 6.0"
+      ],
+      "guarantees": [
+        "-p2_a - p4_a + p5_a <= 2.0",
+        "-p2_e - p4_e + p5_e <= 0.1875",
+        "-p4_a + p5_a <= 7.75",
+        "p5_a <= 7.875"
+      ]
+    }
+  },
+  {
+    "name": "contractsys",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25"
+      ],
+      "guarantees": [
+        "-p4_a + p5_a <= 5.75",
+        "-p4_e + p5_e <= 0.1875"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition4.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition4.json
new file mode 100644
index 00000000..4f0a5c90
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample1composition4.json
@@ -0,0 +1,56 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p4_a",
+        "p4_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p4_a <= 0.0625",
+        "-p4_a <= 0.0",
+        "p4_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p4_a",
+        "p4_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p4_a <= 0.25"
+      ],
+      "guarantees": [
+        "-p4_a + p5_a <= 5.75",
+        "-p4_e + p5_e <= 0.1875"
+      ]
+    }
+  },
+  {
+    "name": "contractsys",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p5_a <= 5.8125",
+        "p5_e <= 0.1875"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition10.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition10.json
new file mode 100644
index 00000000..c68e5a8d
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition10.json
@@ -0,0 +1,70 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e",
+        "mult_out3_a",
+        "mult_out3_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "mult_out2_e <= 0.021533203124999956",
+        "mult_out2_a <= 0.58447265625",
+        "mult_out3_e <= 0.02768554687499991",
+        "mult_out3_a <= 0.1845703125"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e"
+      ],
+      "output_vars": [
+        "add_out1_a",
+        "add_out1_e"
+      ],
+      "assumptions": [
+        "mult_out1_a + mult_out2_a <= 1.0"
+      ],
+      "guarantees": [
+        "add_out1_e - mult_out1_e - mult_out2_e <= 0.0",
+        "add_out1_a <= 0.984375",
+        "add_out1_a - mult_out1_a - mult_out2_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out3_a",
+        "mult_out3_e",
+        "add_out1_a",
+        "add_out1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out3_e <= 0.02768554687499991",
+        "mult_out3_a <= 0.1845703125",
+        "add_out1_e <= 0.04921875000000009",
+        "add_out1_a <= 0.76904296875"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition11.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition11.json
new file mode 100644
index 00000000..05c59de0
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition11.json
@@ -0,0 +1,62 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out3_a",
+        "mult_out3_e",
+        "add_out1_a",
+        "add_out1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out3_e <= 0.02768554687499991",
+        "mult_out3_a <= 0.1845703125",
+        "add_out1_e <= 0.04921875000000009",
+        "add_out1_a <= 0.76904296875"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "add_out1_a",
+        "add_out1_e",
+        "mult_out3_a",
+        "mult_out3_e"
+      ],
+      "output_vars": [
+        "add_out2_a",
+        "add_out2_e"
+      ],
+      "assumptions": [
+        "add_out1_a + mult_out3_a <= 1.0"
+      ],
+      "guarantees": [
+        "-add_out1_e + add_out2_e - mult_out3_e <= 0.0",
+        "add_out2_a <= 0.984375",
+        "-add_out1_a + add_out2_a - mult_out3_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "add_out2_a",
+        "add_out2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "add_out2_e <= 0.076904296875",
+        "add_out2_a <= 0.95361328125"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition5.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition5.json
new file mode 100644
index 00000000..22f05a6d
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition5.json
@@ -0,0 +1,55 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "in1_a",
+        "in1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "in1_a <= 0.984375",
+        "-in1_a <= 0.0",
+        "in1_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "in1_a",
+        "in1_e"
+      ],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "-0.012500000000000011 in1_a - 0.175 in1_e + mult_out1_e <= 0.015380859375",
+        "mult_out1_a <= 0.984375",
+        "-0.1875 in1_a + mult_out1_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition6.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition6.json
new file mode 100644
index 00000000..4436412f
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition6.json
@@ -0,0 +1,56 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "in2_a",
+        "in2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "in2_a <= 0.984375",
+        "-in2_a <= 0.0",
+        "in2_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "in2_a",
+        "in2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "in2_a <= 0.984375",
+        "-in2_a <= 0.0",
+        "in2_e = 0.0"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition7.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition7.json
new file mode 100644
index 00000000..6644cbb1
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition7.json
@@ -0,0 +1,63 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "in2_a",
+        "in2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "in2_a <= 0.984375",
+        "-in2_a <= 0.0",
+        "in2_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "in2_a",
+        "in2_e"
+      ],
+      "output_vars": [
+        "mult_out2_a",
+        "mult_out2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "-0.006249999999999978 in2_a - 0.5875 in2_e + mult_out2_e <= 0.015380859375",
+        "mult_out2_a <= 0.984375",
+        "-0.59375 in2_a + mult_out2_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "mult_out2_e <= 0.021533203124999956",
+        "mult_out2_a <= 0.58447265625"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition8.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition8.json
new file mode 100644
index 00000000..3400729d
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition8.json
@@ -0,0 +1,64 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "mult_out2_e <= 0.021533203124999956",
+        "mult_out2_a <= 0.58447265625"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "in3_a",
+        "in3_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "in3_a <= 0.984375",
+        "-in3_a <= 0.0",
+        "in3_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e",
+        "in3_a",
+        "in3_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "mult_out2_e <= 0.021533203124999956",
+        "mult_out2_a <= 0.58447265625",
+        "in3_a <= 0.984375",
+        "-in3_a <= 0.0",
+        "in3_e = 0.0"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition9.json b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition9.json
new file mode 100644
index 00000000..561369d8
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_composition_success_dspexample2composition9.json
@@ -0,0 +1,71 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e",
+        "in3_a",
+        "in3_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "mult_out2_e <= 0.021533203124999956",
+        "mult_out2_a <= 0.58447265625",
+        "in3_a <= 0.984375",
+        "-in3_a <= 0.0",
+        "in3_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "in3_a",
+        "in3_e"
+      ],
+      "output_vars": [
+        "mult_out3_a",
+        "mult_out3_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "-0.012500000000000011 in3_a - 0.175 in3_e + mult_out3_e <= 0.015380859375",
+        "mult_out3_a <= 0.984375",
+        "-0.1875 in3_a + mult_out3_a <= 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "mult_out1_a",
+        "mult_out1_e",
+        "mult_out2_a",
+        "mult_out2_e",
+        "mult_out3_a",
+        "mult_out3_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "mult_out1_e <= 0.02768554687499991",
+        "mult_out1_a <= 0.1845703125",
+        "mult_out2_e <= 0.021533203124999956",
+        "mult_out2_a <= 0.58447265625",
+        "mult_out3_e <= 0.02768554687499991",
+        "mult_out3_a <= 0.1845703125"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_merging_success_1.json b/tests/test_data/polyhedral_contracts/test_merging_success_1.json
new file mode 100644
index 00000000..e521b34f
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_merging_success_1.json
@@ -0,0 +1,53 @@
+[
+  {
+    "name": "c1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "d2_entry"
+      ],
+      "output_vars": [
+        "d2_exit"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "-d2_entry + d2_exit = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "c2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "c2_entry"
+      ],
+      "output_vars": [
+        "c2_exit"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "-c2_entry + c2_exit = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "c12",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "d2_entry",
+        "c2_entry"
+      ],
+      "output_vars": [
+        "d2_exit",
+        "c2_exit"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "-d2_entry + d2_exit = 0.0",
+        "-c2_entry + c2_exit = 0.0"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_quotient_success_dspexample1quotient1.json b/tests/test_data/polyhedral_contracts/test_quotient_success_dspexample1quotient1.json
new file mode 100644
index 00000000..ec134b28
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_quotient_success_dspexample1quotient1.json
@@ -0,0 +1,56 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p5_e <= 0.1"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p1_a",
+        "p1_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p1_a <= 2.0",
+        "-p1_a <= 0.0",
+        "p1_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p1_a",
+        "p1_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p1_a <= 2.0",
+        "-p1_a <= 0.0",
+        "p1_e = 0.0"
+      ],
+      "guarantees": [
+        "p5_e <= 0.10000000000000009"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_data/polyhedral_contracts/test_quotient_success_dspexample1quotient2.json b/tests/test_data/polyhedral_contracts/test_quotient_success_dspexample1quotient2.json
new file mode 100644
index 00000000..5749c51e
--- /dev/null
+++ b/tests/test_data/polyhedral_contracts/test_quotient_success_dspexample1quotient2.json
@@ -0,0 +1,68 @@
+[
+  {
+    "name": "contract1",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p1_a",
+        "p1_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p1_a <= 2.0",
+        "-p1_a <= 0.0",
+        "p1_e = 0.0"
+      ],
+      "guarantees": [
+        "p5_e <= 0.10000000000000009"
+      ]
+    }
+  },
+  {
+    "name": "contract2",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [],
+      "output_vars": [
+        "p2_a",
+        "p2_e"
+      ],
+      "assumptions": [],
+      "guarantees": [
+        "p2_a <= 3.75",
+        "-p2_a <= 0.0",
+        "p2_e = 0.0"
+      ]
+    }
+  },
+  {
+    "name": "contract3",
+    "type": "PolyhedralContract",
+    "data": {
+      "input_vars": [
+        "p1_a",
+        "p1_e",
+        "p2_a",
+        "p2_e"
+      ],
+      "output_vars": [
+        "p5_a",
+        "p5_e"
+      ],
+      "assumptions": [
+        "p1_a <= 2.0",
+        "-p1_a <= 0.0",
+        "p1_e = 0.0",
+        "p2_a <= 3.75",
+        "-p2_a <= 0.0",
+        "p2_e = 0.0"
+      ],
+      "guarantees": [
+        "p5_e <= 0.10000000000000009"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/tests/test_examples.py b/tests/test_examples.py
index 74b04903..6e197392 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -2,7 +2,7 @@
 from pacti.terms.polyhedra import *
 
 
-def test_examples():
+def test_examples() -> None:
     contract1 = {
         "input_vars": ["u_1"],
         "output_vars": ["x_1"],
diff --git a/tests/test_iocontract.py b/tests/test_iocontract.py
index a4d77e1a..c97e470d 100644
--- a/tests/test_iocontract.py
+++ b/tests/test_iocontract.py
@@ -2,11 +2,11 @@
 from pacti.terms.polyhedra import *
 
 
-def validate_iocontract(contract):
+def validate_iocontract(contract: object) -> bool:
     return isinstance(contract, iocontract.IoContract)
 
 
-def create_contracts(num=1) -> list[dict]:
+def create_contracts(num: int = 1) -> list[dict]:
     """
     Creates `num` number of contracts and returns a list of dicts
     """
@@ -22,11 +22,11 @@ def create_contracts(num=1) -> list[dict]:
     return contracts
 
 
-def test_validate_iocontract():
+def test_validate_iocontract() -> None:
     [c_1, c_2] = [PolyhedralContract.from_dict(c) for c in create_contracts(num=2)]
     assert validate_iocontract(c_1)
     assert validate_iocontract(c_2)
 
 
-def test_contract_equality():
+def test_contract_equality() -> None:
     pass
diff --git a/tests/test_multiple_composition.py b/tests/test_multiple_composition.py
new file mode 100644
index 00000000..d6501cc4
--- /dev/null
+++ b/tests/test_multiple_composition.py
@@ -0,0 +1,24 @@
+from pacti.terms.polyhedra.polyhedral_contract import PolyhedralContract
+from pacti.utils.multiple_composition import compose_multiple_contracts
+
+
+def test_multiple_composition() -> None:
+    c1 = PolyhedralContract.from_string(
+        assumptions=["x <= 1"], guarantees=["y <= 0"], input_vars=["x"], output_vars=["y"]
+    )
+    c2 = PolyhedralContract.from_string(
+        assumptions=["y <= 0"], guarantees=["z <= 0"], input_vars=["y"], output_vars=["z"]
+    )
+    c3 = PolyhedralContract.from_string(
+        assumptions=["y <= 0"], guarantees=["q <= 0"], input_vars=["y"], output_vars=["q"]
+    )
+    c4 = PolyhedralContract.from_string(
+        assumptions=["q <= 0"], guarantees=["v <= 0"], input_vars=["q"], output_vars=["v"]
+    )
+
+    contract = compose_multiple_contracts([c1, c2, c3, c4])
+
+    assert str(contract.a.to_str_list()[0]) == "y <= 0.0"
+    assert str(contract.g.to_str_list()[0]) == "z <= 0.0"
+    assert contract.inputvars[0].name == "y"
+    assert contract.outputvars[0].name == "z"
diff --git a/tests/test_polyhedral_contract.py b/tests/test_polyhedral_contract.py
index 27b7a68d..f193f7da 100644
--- a/tests/test_polyhedral_contract.py
+++ b/tests/test_polyhedral_contract.py
@@ -11,7 +11,7 @@
 
 
 @pytest.mark.parametrize("test_instance", composition_test_instances)
-def test_composition_success(test_instance):
+def test_composition_success(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
@@ -29,7 +29,7 @@ def test_composition_success(test_instance):
 
 
 @pytest.mark.parametrize("test_instance", composition_failure_test_instances)
-def test_composition_failure(test_instance):
+def test_composition_failure(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
@@ -43,7 +43,7 @@ def test_composition_failure(test_instance):
 
 
 @pytest.mark.parametrize("test_instance", quotient_test_instances)
-def test_quotient_success(test_instance):
+def test_quotient_success(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
@@ -61,7 +61,7 @@ def test_quotient_success(test_instance):
 
 
 @pytest.mark.parametrize("test_instance", quotient_failure_test_instances)
-def test_quotient_failure(test_instance):
+def test_quotient_failure(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
@@ -75,7 +75,7 @@ def test_quotient_failure(test_instance):
 
 
 @pytest.mark.parametrize("test_instance", merging_test_instances)
-def test_merging_success(test_instance):
+def test_merging_success(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
@@ -93,7 +93,7 @@ def test_merging_success(test_instance):
 
 
 @pytest.mark.parametrize("test_instance", merging_failure_test_instances)
-def test_merging_failure(test_instance):
+def test_merging_failure(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
@@ -107,7 +107,7 @@ def test_merging_failure(test_instance):
 
 
 @pytest.mark.parametrize("test_instance", refinement_test_instances)
-def test_refinement(test_instance):
+def test_refinement(test_instance: str) -> None:
     try:
         c, _ = read_contracts_from_file(test_instance)
     except:
diff --git a/tests/test_polyhedral_contract_syntax.py b/tests/test_polyhedral_contract_syntax.py
index e23927fd..608bd095 100644
--- a/tests/test_polyhedral_contract_syntax.py
+++ b/tests/test_polyhedral_contract_syntax.py
@@ -1,7 +1,7 @@
 from pacti.terms.polyhedra import *
 
 
-def test_empty_contract():
+def test_empty_contract() -> None:
     c = PolyhedralContract.from_string(input_vars=[], output_vars=[], assumptions=[], guarantees=[])
     assert 0 == len(c.inputvars)
     assert 0 == len(c.outputvars)
@@ -9,7 +9,7 @@ def test_empty_contract():
     assert 0 == len(c.g.terms)
 
 
-def test_simple_contract():
+def test_simple_contract() -> None:
     c = PolyhedralContract.from_string(input_vars=["x"], output_vars=[], assumptions=["-3x <= 0"], guarantees=[])
     assert 1 == len(c.inputvars)
     assert "x" == c.inputvars[0].name
@@ -25,7 +25,7 @@ def test_simple_contract():
 
 
 # | LHS | <= RHS
-def test_pattern2_contract():
+def test_pattern2_contract() -> None:
     c = PolyhedralContract.from_string(input_vars=["x"], output_vars=[], assumptions=["|x| <= 0"], guarantees=[])
     assert 1 == len(c.inputvars)
     assert "x" == c.inputvars[0].name
@@ -49,7 +49,7 @@ def test_pattern2_contract():
 
 
 # | LHS | = 0
-def test_pattern3_contract():
+def test_pattern3_contract() -> None:
     c = PolyhedralContract.from_string(input_vars=["x"], output_vars=[], assumptions=["|x| = 0"], guarantees=[])
     assert 1 == len(c.inputvars)
     assert "x" == c.inputvars[0].name
@@ -73,7 +73,7 @@ def test_pattern3_contract():
 
 
 # LHS = RHS
-def test_pattern4_contract():
+def test_pattern4_contract() -> None:
     c = PolyhedralContract.from_string(input_vars=["x"], output_vars=[], assumptions=["x = 1"], guarantees=[])
     assert 1 == len(c.inputvars)
     assert "x" == c.inputvars[0].name
diff --git a/tests/test_polyhedral_terms.py b/tests/test_polyhedral_terms.py
index d4ffb364..71c57406 100644
--- a/tests/test_polyhedral_terms.py
+++ b/tests/test_polyhedral_terms.py
@@ -1,45 +1,20 @@
 import logging
 
 import pytest
-import sympy as sympy
-from sympy.parsing.sympy_parser import parse_expr
 
 from pacti.iocontract import Var
-from pacti.terms.polyhedra import PolyhedralTerm, PolyhedralTermList
+from pacti.terms.polyhedra import PolyhedralTermList
+from pacti.terms.polyhedra.serializer import polyhedral_termlist_from_string
 
 FORMAT = "%(asctime)s:%(levelname)s:%(name)s:%(message)s"
 logging.basicConfig(filename="../pacti.log", filemode="w", level=logging.DEBUG, format=FORMAT)
 
 
-def to_pt(str_rep):
-    expr = parse_expr(str_rep)
-    assert isinstance(expr, sympy.core.relational.LessThan)
-    constant = expr.args[1]
-    print(type(constant))
-    variables = {}
-    for k, v in expr.args[0].as_coefficients_dict().items():
-        if k == 1:
-            pass
-        elif isinstance(k, sympy.core.symbol.Symbol):
-            variables[str(k)] = v
-        elif isinstance(k, sympy.core.mul.Mul):
-            if isinstance(k.args[1], k, sympy.core.symbol.Symbol):
-                print(k.args[0])
-                variables[str(k.args[1])] = k.args[0]
-            elif isinstance(k.args[0], k, sympy.core.symbol.Symbol):
-                print(k.args[1])
-                variables[str(k.args[0])] = k.args[1]
-        else:
-            raise ValueError
-    return PolyhedralTerm(variables, constant)
-
-
-def to_pts(str_rep_list):
-    terms = [to_pt(str_rep) for str_rep in str_rep_list]
-    return PolyhedralTermList(terms)
-
-
-def test_polyhedral_var_elim_by_refinement_1():
+def to_pts(str_rep_list: list[str]) -> PolyhedralTermList:
+    return PolyhedralTermList([item for el in str_rep_list for item in polyhedral_termlist_from_string(el)])
+
+
+def test_polyhedral_var_elim_by_refinement_1() -> None:
     x = Var("x")
     # the context cannot simplify or transform the reference
     reference = to_pts(["2*x <= 4"])
@@ -50,7 +25,7 @@ def test_polyhedral_var_elim_by_refinement_1():
     assert reference.terms == expected.terms
 
 
-def test_polyhedral_var_elim_by_refinement_2():
+def test_polyhedral_var_elim_by_refinement_2() -> None:
     x = Var("x")
     # the context can simplify but not transform the reference
     reference = to_pts(["2*x <= 4"])
@@ -61,7 +36,7 @@ def test_polyhedral_var_elim_by_refinement_2():
     assert reference.terms == expected.terms
 
 
-def test_polyhedral_var_elim_by_refinement_3():
+def test_polyhedral_var_elim_by_refinement_3() -> None:
     # the context can transform the reference
     x = Var("x")
     y = Var("y")
@@ -73,7 +48,7 @@ def test_polyhedral_var_elim_by_refinement_3():
     assert reference.terms == expected.terms
 
 
-def test_polyhedral_var_elim_by_refinement_4():
+def test_polyhedral_var_elim_by_refinement_4() -> None:
     # a term that cannot be simplified
     x = Var("x")
     reference = to_pts(["x <= -1"])
@@ -84,7 +59,7 @@ def test_polyhedral_var_elim_by_refinement_4():
     assert reference.terms == expected.terms
 
 
-def test_polyhedral_var_elim_by_refinement_5():
+def test_polyhedral_var_elim_by_refinement_5() -> None:
     # a term that can be simplified with one element of the context
     x = Var("x")
     reference = to_pts(["x <= -1"])
@@ -95,7 +70,7 @@ def test_polyhedral_var_elim_by_refinement_5():
     assert reference.terms == expected.terms
 
 
-def test_polyhedral_var_elim_by_refinement_6():
+def test_polyhedral_var_elim_by_refinement_6() -> None:
     # a term that can be simplified with one element of the context
     x = Var("x")
     z = Var("z")
@@ -107,7 +82,7 @@ def test_polyhedral_var_elim_by_refinement_6():
     assert reference.terms == expected.terms
 
 
-def test_polyhedral_var_elim_by_relaxation_7():
+def test_polyhedral_var_elim_by_relaxation_7() -> None:
     # a term that can be simplified with one element of the context
     x = Var("x")
     y = Var("y")
@@ -120,27 +95,14 @@ def test_polyhedral_var_elim_by_relaxation_7():
     assert reference.terms == expected.terms
 
 
-# def test_polyhedral_var_elim_by_refinement_8():
-#    # a term that can be simplified with one element of the context
-#    y = Var("y")
-#    z = Var("z")
-#    reference = to_pts(["x + -z + y <= -1"])
-#    context = to_pts(["4*y - 9*z <= -2", "5*y - z <= 2"])
-#    # the expected value is wrong
-#    expected = to_pts(["x <= -0.2"])
-#    vars_elim = {y, z}
-#    reference = reference.elim_vars_by_refining(context, vars_elim)
-#    assert reference.terms == expected.terms
-
-
-def test_simplify_1():
-    reference = to_pts(["-1/2*i <= -3/2", "3*i <= 1", "-1*i <= 2", "1*i <= 2"])
-    expected = to_pts(["-1/2*i <= -3/2", "3*i <= 1"])
+def test_simplify_1() -> None:
+    reference = to_pts(["-0.5i <= -1.5", "3*i <= 1", "-1*i <= 2", "1*i <= 2"])
+    expected = to_pts(["-0.5*i <= -1.5", "3*i <= 1"])
     with pytest.raises(ValueError):
         reference.simplify()
 
 
-def test_issue171():
+def test_issue171() -> None:
     constraints = to_pts(
         ["-1*dt0 - 1*t0 <= 0.0", "-1*t0 <= 0.0", "-1*dt0 - 1*t0 + 1*t1 <= 0.0", "1*dt0 + 1*t0 - 1*t1 <= 0.0"]
     )
diff --git a/tests/test_py_loaders.py b/tests/test_py_loaders.py
index bb58cf60..40c60347 100644
--- a/tests/test_py_loaders.py
+++ b/tests/test_py_loaders.py
@@ -5,7 +5,7 @@
 from pacti.terms.polyhedra import *
 
 
-def create_contracts(num=1) -> list[dict]:
+def create_contracts(num: int = 1) -> list[dict]:
     """
     Creates `num` number of contracts and returns a list of dicts
     """
@@ -22,7 +22,7 @@ def create_contracts(num=1) -> list[dict]:
     return contracts
 
 
-def test_read_contract():
+def test_read_contract() -> None:
     """
     Test read_contract
     """
@@ -39,6 +39,6 @@ def test_read_contract():
         assert isinstance(io_c, iocontract.IoContract)
         assert validate_iocontract(io_c)
     # Ensure that all contracts are dictionaries
-    c_i = [("input_vars", "u"), ("output_vars", "x")]
-    with pytest.raises(ValueError, match="A dict type contract is expected."):
-        PolyhedralContract.from_dict(c_i)
+    c_n = {"input_vars": "u", "output_vars": "x"}
+    with pytest.raises(ValueError, match="Passed dictionary does not have key assumptions."):
+        PolyhedralContract.from_dict(c_n)