Repository: cda-tum/mqt-bench (language: python)

Issue:
Versioning
To improve the versioning of MQT Bench, the following task should be implemented:

- [x] Instead of creating the ZIP using the GitHub actions, a ZIP should be added to each release
- [x] The workflow for publishing updates of the UI without a newly created ZIP file needs to be adapted
- [x] Benchviewer should be part of the pypi package such that it may be executed locally (also very important for reproducing a certain MQT Bench version). The viewer and the ZIP file may be optional dependencies for the pypi package.

Update Process for Benchviewer Requirements on Server
Currently the deployment only copies the necessary files to the server and restarts the wsgi application. Updates to depencies such as `flask` are not handled.

Are we using a [virtualenv](https://docs.python.org/3/library/venv.html) on the server, where a release can trigger a `pip install --upgrade -r requirements.txt`?

Alternatively, if we package benchviewer on PyPI as well, the server can run `pip install -U mqt.bench` in the venv.
Remove excitedstate
solves #82 


Discussion / hints:
We can use the [LZMA](https://docs.python.org/3/library/zipfile.html#zipfile.ZIP_LZMA) compresion algorithm for the zip as well. In a brief test, this reduces the size of the whole archive from 1.4GB to 0.7GB compared to the current [deflated](https://docs.python.org/3/library/zipfile.html#zipfile.ZIP_DEFLATED) algorithm. For the generated zip in the viewer, we should still use deflated though, as LZMA compression is way slower. Decompression seems to be neglectably slower.

Benchmark used: https://gist.github.com/hillmich/d1c8a1f397758bda3c17885841741aca



Fix patch:
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 0cee4cca7..f3ea660af 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -11,15 +11,13 @@ jobs:
   run:
     runs-on: ubuntu-latest
     steps:
-    - uses: snow-actions/sparse-checkout@v1.1.0
-      with:
-        patterns: "mqt"
+    - uses: actions/checkout@v3
     - name: Setup Python
-      uses: actions/setup-python@v3
+      uses: actions/setup-python@v4
       with:
-        python-version: '3.9'
+        python-version: '3.8'
     - name: Install MQT Bench
-      run: pip install .\[all\]
+      run: pip install -e .
     - name: Generate Report
       run: |
         pip install pytest-cov
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c27947a4c..fa537b90d 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -14,17 +14,19 @@ jobs:
     name:    Build wheel
     runs-on: ubuntu-latest
     steps:
-      - uses: snow-actions/sparse-checkout@v1.1.0
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
         with:
-          patterns: "mqt"
-      - uses: actions/setup-python@v3
+          python-version: '3.8'
         name: Install Python
-      - name: Install dependencies
-        run: python -m pip install build
       - name: Build wheel
-        run:  python -m build --wheel
+        run: pipx run build --wheel
       - name: Install wheel
         run: python -m pip install --verbose dist/*.whl
+      - name: Run tests
+        run: |
+          pip install pytest
+          pytest mqt/benchviewer/src/tests/test_backend.py -k 'test_flask_server'
       - uses: actions/upload-artifact@v3
         with:
           path: dist/*.whl
@@ -33,17 +35,19 @@ jobs:
     name:    Build source distribution
     runs-on: ubuntu-latest
     steps:
-      - uses: snow-actions/sparse-checkout@v1.1.0
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
         with:
-          patterns: "mqt"
-      - uses: actions/setup-python@v3
+          python-version: '3.8'
         name: Install Python
-      - name: Install dependencies
-        run: python -m pip install build
       - name: Build sdist
-        run:  python -m build --sdist
+        run: pipx run build --sdist
       - name: Install sdist
         run: python -m pip install --verbose dist/*.tar.gz
+      - name: Run tests
+        run: |
+          pip install pytest
+          pytest mqt/benchviewer/src/tests/test_backend.py -k 'test_flask_server'
       - uses: actions/upload-artifact@v3
         with:
           path: dist/*.tar.gz
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index 37b3cd414..5b36faa7a 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -6,9 +6,9 @@ jobs:
   Black:
     runs-on: ubuntu-latest
     steps:
-      - uses: snow-actions/sparse-checkout@v1.1.0
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
         with:
-          patterns: "mqt"
-      - uses: actions/setup-python@v2
+          python-version: '3.8'
       - name: Black
         uses: psf/black@stable
diff --git a/.github/workflows/server_deployment.yml b/.github/workflows/server_deploy.yml
similarity index 52%
rename from .github/workflows/server_deployment.yml
rename to .github/workflows/server_deploy.yml
index 5aa4b3b1e..26e105a3c 100644
--- a/.github/workflows/server_deployment.yml
+++ b/.github/workflows/server_deploy.yml
@@ -11,9 +11,7 @@ env:
   KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
   CDA_HOST_NAME: tueicda-cda.srv.mwn.de
   CDA_USER_NAME: web-user
-  CDA_TARGET_DIR: /var/www/cda/app/mqtbench/
-  CDA_TARGET_DIR_FILES: /local/mqtbench/qasm_output
-  CDA_SOURCE_DIR_FILES: benchviewer/static/files/qasm_output
+  CDA_TARGET_DIR: /var/www/cda/app/
   CDA_GIT_DIR: .git/
 
 jobs:
@@ -21,17 +19,14 @@ jobs:
     name: Deploy
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
-    - name: Creating zip file
-      run:  zip -rjv ./benchviewer/static/files/qasm_output/MQTBench_all.zip ./benchviewer/static/files/qasm_output/ ./benchviewer/static/files/algo_level.txt
     - name: Setup Python
-      uses: actions/setup-python@v3
+      uses: actions/setup-python@v4
       with:
-        python-version: '3.9'
+        python-version: '3.8'
     - name: Install wheel
       run: pip install wheel
     - name: Install MQT Bench
-      run: pip install .\[all\]
+      run: pip install .
     - name: Run tests
       run: |
         pip install pytest-cov
@@ -43,10 +38,8 @@ jobs:
         echo "${DEPLOY_KEY}" > ~/.ssh/my_rsync_key
         echo "IdentityFile ~/.ssh/my_rsync_key" >> ~/.ssh/config
         chmod -R 700 ~/.ssh
-    - name: rsync deployment
+    - name: Deployment on Webserver
       working-directory: ${{ github.workspace }}
       run: |
-        rsync -avz --update -e ssh ${CDA_SOURCE_DIR_FILES}/MQTBench_all.zip ${CDA_USER_NAME}@${CDA_HOST_NAME}:${CDA_TARGET_DIR_FILES}/MQTBench_all.zip
-        rsync -avz --update -e ssh config.json ${CDA_USER_NAME}@${CDA_HOST_NAME}:${CDA_TARGET_DIR}/config.json
-        rsync -avz --update -e ssh --cvs-exclude --exclude "*__pycache__/" --exclude "*.qasm" --exclude ${CDA_SOURCE_DIR_FILES} ./benchviewer ${CDA_USER_NAME}@${CDA_HOST_NAME}:${CDA_TARGET_DIR}
+        rsync -avz --update -e ssh --cvs-exclude --exclude "*__pycache__/" --exclude "*.qasm" . ${CDA_USER_NAME}@${CDA_HOST_NAME}:${CDA_TARGET_DIR}
 
diff --git a/.gitignore b/.gitignore
index 22fd893ae..29af4662f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,8 +5,9 @@
 ### Manually added
 /hist_output/*
 /qasm_output/*
-/benchviewer/static/files/qasm_output_big
-/benchviewer/static/files/qasm_output_small
+
+*.qasm
+
 .idea/*
 *.DS_Store
 *hist_output/*png
diff --git a/.lgtm.yml b/.lgtm.yml
index 11ce00b71..ff683ac40 100644
--- a/.lgtm.yml
+++ b/.lgtm.yml
@@ -2,4 +2,3 @@ path_classifiers:
   test:
     - test
     - scripts
-  library:
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 000000000..4f99ca230
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include mqt/benchviewer/static/files/MQTBench_all.zip
+include mqt/benchviewer/templates/*
diff --git a/README.md b/README.md
index 6b404a80e..666ca42eb 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ MQT Bench is part of the Munich Quantum Toolkit (MQT) developed by the [Chair fo
 
 [<img src="img/mqtbench.png" align="center" width="500" >](https://www.cda.cit.tum.de/mqtbench)
 
+This documentation explains how to use MQT Bench to create and filter benchmarks.
+
 ## Abstraction Levels
 It uses the structure proposed by the openQASM 3.0 specification [[1]](https://arxiv.org/abs/2104.14722) and offers benchmarks
 on four different abstraction levels:
@@ -73,7 +75,6 @@ This circuit is now executable on the IBMQ Manila device, since all hardware ind
 So far, the following benchmarks are implemented and provided:
 - Amplitude Estimation
 - Deutsch-Jozsa
-- Excited State
 - GHZ State
 - Graph State
 - Ground State
@@ -103,22 +104,34 @@ So far, the following benchmarks are implemented and provided:
 
 See the [benchmark description](https://www.cda.cit.tum.de/mqtbench/benchmark_description) for further details on the individual benchmarks.
 
+## Quantum Circuit Compiler Support
+At the moment, two compilers are supported:
+1) [Qiskit](https://qiskit.org/documentation/) with the compiler settings: Optimization level 0 to 3
+2) [TKET](https://cqcl.github.io/tket/pytket/api/) with the compiler settings: Line placement and graph placement
+
 ## Native Gate-Set Support
 So far, MQT Bench supports the following native gate-sets:
-1) IBMQ gate-set: *\['id', 'rz', 'sx', 'x', 'cx', 'reset'\]*
-2) Rigetti gate-set: *\['id, 'rx', 'rz', 'cz', 'reset'\]*
-
-## Mapping Scheme Support
-Currently, MQT Bench supports two mapping schemes:
-1) Smallest Fitting Architecture Mapping: Maps quantum circuits to the smallest architecture with a sufficient number of physical qubits
-2) Biggest Architecture Mapping: Always use the biggest available hardware architecture
+1) IBMQ gate-set: *\['rz', 'sx', 'x', 'cx', 'measure'\]*
+2) Rigetti gate-set: *\['rx', 'rz', 'cz', 'measure'\]*
+3) IonQ gate-set: *\['rxx', 'rz', 'ry', 'rx', 'measure'\]*
+4) OQC gate-set: *\['rz', 'sx', 'x', 'ecr', 'measure'\]*
+
+## Device Support
+So far, MQT Bench supports the following devices:
+1) IBMQ Washington with 127 qubits
+2) IBMQ Montreal with 27 qubits
+3) Rigetti Aspen-M1 with 80 qubits
+4) IonQ with 11 qubits
+5) OQC Lucy with 8 qubits
 
 # Repository Structure
 - mqt/bench/utils: Directory for the utils.py file 
 - mqt/bench/tests: Directory for the tests for MQT Bench
 - mqt/bench/benchmarks: On the top-level, each benchmark algorithm is included as a separate file. 
   - Additionally, folders for each IBM Qiskit application module and their respective benchmarks are listed.
-- benchviewer: This is the folder for our webpage hosted at [https://www.cda.cit.tum.de/mqtbench/](https://www.cda.cit.tum.de/mqtbench/).
+- mqt/benchviewer: This is the folder for the webpage 
+(which can be started locally and is also hosted at 
+[https://www.cda.cit.tum.de/mqtbench/](https://www.cda.cit.tum.de/mqtbench/)).
 
 ```
 MQTBench/
@@ -143,13 +156,15 @@ MQTBench/
 │       └─── qiskit_application_optimization
 │       │       ...
 │
-│───benchviewer/
+│───────benchviewer/
+        │ - main.py
+        │   ...
 ```
 
 # Repository Usage
 There are three ways how to use this benchmark suite:
-1) Via our web-interface hosted at [https://www.cda.cit.tum.de/mqtbench/](https://www.cda.cit.tum.de/mqtbench/)
-2) Via our pip package `mqt.bench`
+1) Via the webpage hosted at [https://www.cda.cit.tum.de/mqtbench/](https://www.cda.cit.tum.de/mqtbench/)
+2) Via the pip package `mqt.bench`
 3) Directly via this repository
 
 Since the first way is rather self-explanatory, the other two ways are explained in more detail in the following.
@@ -167,48 +182,104 @@ get_one_benchmark(
     level: Union[str, int],
     circuit_size: int = None,
     benchmark_instance_name: str = None,
-    opt_level: int = None,
-    gate_set_name: str = None,
-    smallest_fitting_arch: bool = None,
-)
+    compiler: str = "qiskit",
+    compiler_settings: dict[str, dict[str, any]] = None,
+    gate_set_name: str = "ibm",
+    device_name: str = "ibm_washington",
+):
 ```
 The available parameters are:
   - `benchmark_name`: `"ae"`, `"dj"`, `"grover-noancilla"`, `"grover-v-chain"`, `"ghz"`, `"graphstate"`, `"portfolioqaoa"`,
                         `"portfoliovqe"`, `"qaoa"`, `"qft"`, `"qftentangled"`, `"qgan"`, `"qpeexact"`, `"qpeinexact"`,
                         `"qwalk-noancilla"`, `"qwalk-v-chain"`, `"realamprandom"`, `"su2random"`, `"twolocalrandom"`, `"vqe"`,
-                        `"wstate"`, `"shor"`, `"hhl"`, `"pricingcall"`, `"pricingput"`, `"groundstate"`, `"excitedstate"`, `"routing"`,
+                        `"wstate"`, `"shor"`, `"hhl"`, `"pricingcall"`, `"pricingput"`, `"groundstate"`, `"routing"`,
                         `"tsp"`
   - `level`: `0` or `"alg"`, `1` or `"indep"`, `2` or `"nativegates"`, `3` or `"mapped"`
-  - `circuit_size`: most of the cases this is equal to number of qubits (for some benchmarks the number of qubits is higher)
-  - `benchmark_instance_name`: `"xsmall"`, `"small"`, `"medium"`, `"large"`, `"xlarge"` (not all instances are available for each benchmark)
-  - `opt_level`: `0`, `1`, `2`, `3`
-  - `gate_set_name`: `"ibm"`, `"rigetti"`
-  - `smallest_fitting_arch`: `False`, `True`
+  - `circuit_size`: for most of the cases this is equal to number of qubits 
+(all scalable benchmarks except `"qwalk-v-chain"` and `"grover-v-chain"`) while for all other the qubit number is higher
+  - `compiler`: `"qiskit"` or `"tket"`
+  - `compiler_settings`: Optimization level for `"qiskit"` (`0`-`3`), placement for `"tket"` (`lineplacement` or `graphplacement`), exemplary shown:
+```python
+compiler_settings = {
+            "qiskit": {"optimization_level": 1},
+        }
+```
+or 
+```python
+compiler_settings = {
+            "tket": {"placement": "lineplacement"},
+        }
+```
 
-For example, in order to obtain the *5*-qubit Deutsch-Josza algorithm, use the following:
+  - `gate_set_name`: `"ibm"`, `"rigetti"`, `"ionq"`, or `"oqc"`
+  - `device_name`: `"ibm_washington"`, `"ibm_montreal"`, `"aspen_m1"`, `"ionq11"`, `"lucy"`
+
+Hereby, the mappings between shortened `benchmark_name` and actual benchmarks are:
+
+| `benchmark_name`   | Actual Benchmark                                   |
+|--------------------|----------------------------------------------------|
+| `"ae"`             | Amplitude Estimation (AE)                          |
+| `"dj"`             | Deutsch-Jozsa                                      |
+| `"grover-noancilla"` | Grover's (no ancilla)                              |
+| `"grover-v-chain"` | Grover's (v-chain)                                 |
+| `"ghz"`            | GHZ State                                          |
+| `"graphstate"`     | Graph State                                        |
+| `"portfolioqaoa"`  | Portfolio Optimization with QAOA                   |
+| `"portfoliovqe"`   | Portfolio Optimization with VQE                    |
+| `"qaoa"`           | Quantum Approximation Optimization Algorithm (QAOA) |
+| `"qft"`            | Quantum Fourier Transformation (QFT)               |
+| `"qftentangled"`   | QFT Entangled                                      |
+| `"qgan"`           | Quantum Generative Adversarial Network             |
+| `"qpeexact"`       | Quantum Phase Estimation (QPE) exact               |
+| `"qpeinexact"`     | Quantum Phase Estimation (QPE) inexact             |
+| `"qwalk-noancilla"` | Quantum Walk (no ancilla)                          |
+| `"qwalk-v-chain"`  | Quantum Walk (v-chain)                             |
+| `"realamprandom"`  | Real Amplitudes ansatz with Random Parameters      |
+| `"su2random"`      | Efficient SU2 ansatz with Random Parameters        |
+| `"twolocalrandom"` | Two Local ansatz with Random Parameters            |
+| `"vqe"`            | Variational Quantum Eigensolver (VQE)              |
+| `"wstate"`         | W-State                                            |
+| `"shor"`            | Shor's                                             |
+| `"hhl"`            | HHL                                                |
+| `"pricingcall"`          | Pricing Call Option                                |
+| `"pricingput"`          | Pricing Put Option                                 |
+| `"groundstate"`          | Ground State                                       |
+| `"routing"`          | Routing                                            |
+| `"tsp"`          | Travelling Salesman                                |
+
+
+
+For example, in order to obtain the *5*-qubit Deutsch-Josza benchmark on algorithm level, use the following:
 ```python
 from mqt.bench import get_one_benchmark
 
 qc = get_one_benchmark("dj", "alg", 5)
 ```
 
-In order to get access to all benchmarks, including the Qiskit application module algorithms, some addtional dependencies are required. 
-These can be installed via pip as follows:
+### Locally hosting the MQT Bench Viewer
+Additionally, this python package includes the same webserver used for the hosting of the 
+[MQT Bench webpage](https://www.cda.cit.tum.de/mqtbench).
+
+After the `mqt.bench` Python package is installed via 
+```console
+(venv) $ pip install mqt.bench
+```
+the MQT Bench Viewer can be started from the terminal via
 ```console
-(venv) $ pip install mqt.bench[all]
+(venv) $ mqt.bench
 ```
 
+This first searches for the most recent version of the benchmark files on GitHub and offers to download them.
+Afterwards, the webserver is started locally.
+
 ## Usage directly via this repository
-Since all generated benchmarks hosted on our website are included in this repository, 
-the repository is very large (`>25 GB`). Therefore, please do a sparse-checkout if you want to 
-directly access the repository itself:
+For that, the repository must be cloned and installed:
 ```
-git clone --filter=blob:none --no-checkout  https://github.com/cda-tum/MQTBench.git
+git clone https://github.com/cda-tum/MQTBench.git
 cd MQTBench
-git sparse-checkout init --cone
-git sparse-checkout set mqt img
-git checkout main
+pip install .
 ```
+Afterwards, the package can be used as described [above](#Usage via pip package).
 
 # References:
 In case you are using MQT Bench in your work, we would be thankful if you referred to it by citing the following publication:
diff --git a/benchviewer/README.md b/benchviewer/README.md
deleted file mode 100644
index dc1838319..000000000
--- a/benchviewer/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# BenchViewer
-BenchViewer is the main interface for user's to filter and download benchmarks from MQT Bench 
-
-# Structure
-- ```main.py```: File to start the server
-- ```backend.py```: Containing the backend logic
-- ```static/files/qasm_output.py```: Containing all available benchmark files
-```
-BenchViewer
-│ - README.md
-│ - backend.py  
-│ - main.py  
-│ - ...
-└───static
-    │ ...
-    └───files
-         └─── qasm_output
-                └─── <all available benchmarks>
-                └─── MQTBench_all.zip
-         
-
-```
-
-# Usage
-Before starting, all benchmarks must be zipped into a `MQTBench_all.zip` archive. 
-To start the website afterwards, just run the jupyter notebook locally:
-```python
-python main.py
-```
-The website is afterwards hosted at [http://127.0.0.1:5000/mqtbench](http://127.0.0.1:5000/mqtbench).
\ No newline at end of file
diff --git a/benchviewer/requirements.txt b/benchviewer/requirements.txt
deleted file mode 100644
index 7a0d65f04..000000000
--- a/benchviewer/requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Flask~=2.1.2
-numpy~=1.22.4
-pandas~=1.4.2
-pytest~=7.1.2
-networkx~=2.8
diff --git a/benchviewer/static/files/algo_level.txt b/benchviewer/static/files/algo_level.txt
deleted file mode 100644
index 74f900fcd..000000000
--- a/benchviewer/static/files/algo_level.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Until openQASM 3.0 is widely supported, all circuits (except those on the algorithmic level) are provided as .qasm files following the openQASM 2.0 standard.
-Circuits on the algorithmic level can directly be generated (as IBM Qiskit QuantumCircuit objects) using the mqt.bench python package.
-
-To get access, please follow these installation and import instructions:
-
-pip install mqt.bench
-from mqt.bench import get_one_benchmark
-
-To generate one benchmark on the algorithmic level, please use this method:
-
-get_one_benchmark(
-    benchmark_name: str,
-    level: Union[str, int],
-    circuit_size: int = None,
-    benchmark_instance_name: str = None,
-    opt_level: int = None,
-    gate_set_name: str = None,
-    smallest_fitting_arch: bool = None,
-)
-
-For example:
-qc = get_one_benchmark("dj", "alg", 5)
-
-The available parameters are:
-    -> benchmark_name: "ae", "dj", "grover-noancilla", "grover-v-chain", "ghz", "graphstate", "portfolioqaoa",
-                        "portfoliovqe", "qaoa", "qft", "qftentangled", "qgan", "qpeexact", "qpeinexact",
-                        "qwalk-noancilla", "qwalk-v-chain", "realamprandom", "su2random", "twolocalrandom", "vqe",
-                        "wstate", "shor", "hhl", "pricingcall", "pricingput", "groundstate", "excitedstate", "routing",
-                        "tsp"
-    -> level: 0 or "alg", 1 or "indep", 2 or "nativegates", 3 or "mapped"
-    -> circuit_size: most of the cases this is equal to number of qubits (for some benchmarks the number of qubits is higher)
-    -> benchmark_instance_name: "xsmall", "small", "medium", "large", "xlarge" (not all instances are available for each benchmark)
-    -> opt_level: 0, 1, 2, 3
-    -> gate_set_name: "ibm", "rigetti"
-    -> smallest_fitting_arch: False, True
-
-
-For more information, visit our MQT Bench webpage on https://www.cda.cit.tum.de/mqtbench/.
\ No newline at end of file
diff --git a/benchviewer/wsgi.py b/benchviewer/wsgi.py
deleted file mode 100644
index df8669fdb..000000000
--- a/benchviewer/wsgi.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from main import app
-
-if __name__ == "__main__":
-    #    app.config["SERVER_NAME"] = "cda.cit.tum.de"
-    #    app.config["APPLICATION_ROOT"] = "/app/benchviewer/"
-    app.run()
diff --git a/img/mqtbench.png b/img/mqtbench.png
index 6c3a35e19..3f03a2538 100644
Binary files a/img/mqtbench.png and b/img/mqtbench.png differ
diff --git a/mqt/bench/__init__.py b/mqt/bench/__init__.py
index 352e7c1a4..ebe64771d 100644
--- a/mqt/bench/__init__.py
+++ b/mqt/bench/__init__.py
@@ -1,1 +1,4 @@
-from .benchmark_generator import get_one_benchmark, create_benchmarks_from_config
+from mqt.bench.benchmark_generator import (
+    get_one_benchmark,
+    create_benchmarks_from_config,
+)
diff --git a/mqt/bench/benchmark_generator.py b/mqt/bench/benchmark_generator.py
index d30c586b4..b5b4ab360 100644
--- a/mqt/bench/benchmark_generator.py
+++ b/mqt/bench/benchmark_generator.py
@@ -1,6 +1,5 @@
 from qiskit import QuantumCircuit, transpile
 
-import matplotlib.pyplot as plt
 import json
 import importlib
 import signal
@@ -10,6 +9,7 @@
 
 from typing import Union
 from os import path, mkdir, remove
+from joblib import Parallel, delayed
 
 from mqt.bench.benchmarks import (
     shor,
@@ -20,7 +20,7 @@
     pricingcall,
     pricingput,
 )
-from mqt.bench.benchmarks.qiskit_application_nature import groundstate, excitedstate
+from mqt.bench.benchmarks.qiskit_application_nature import groundstate
 
 
 from mqt.bench.utils import qiskit_helper, tket_helper, utils
@@ -58,7 +58,6 @@ def create_benchmarks_from_config(cfg_path: str):
         cfg = json.load(jsonfile)
         print("Read config successful")
 
-    # global seetings
     global timeout
     timeout = cfg["timeout"]
 
@@ -67,16 +66,10 @@ def create_benchmarks_from_config(cfg_path: str):
     if not path.isdir(qasm_output_folder):
         mkdir(qasm_output_folder)
 
-    # for benchmark in cfg["benchmarks"]:
-    # print(benchmark["name"])
-    # generate_benchmark(benchmark)
-
-    from joblib import Parallel, delayed
-
     Parallel(n_jobs=-1, verbose=100)(
         delayed(generate_benchmark)(benchmark) for benchmark in cfg["benchmarks"]
     )
-    return
+    return True
 
 
 def benchmark_generation_watcher(func, args):
@@ -88,16 +81,27 @@ def timeout_handler(signum, frame):  # Custom signal handler
 
     # Change the behavior of SIGALRM
     signal.signal(signal.SIGALRM, timeout_handler)
-
     signal.alarm(timeout)
     try:
         res = func(*args)
     except TimeoutException:
-        print("Calculation/Generation exceeded timeout limit for ", func, args[1:])
+        print(
+            "Calculation/Generation exceeded timeout limit for ",
+            func.__name__,
+            func.__module__.split(".")[-1],
+            args[0].name,
+            args[1:],
+        )
         return False
     except Exception as e:
-        # print("Calculation/Generation exceeded timeout limit for ", func, args[1:])
-        print("Exception: ", e)  # , func, args[0].name, args[1:])
+        print(
+            "Exception: ",
+            e,
+            func.__name__,
+            func.__module__.split(".")[-1],
+            args[0].name,
+            args[1:],
+        )
         return False
     else:
         # Reset the alarm
@@ -195,15 +199,6 @@ def generate_benchmark(benchmark):
                 if not res:
                     break
 
-        elif benchmark["name"] == "excitedstate":
-            for choice in benchmark["instances"]:
-                res_qc_creation = qc_creation_watcher(create_excitedstate_qc, [choice])
-                if not res_qc_creation:
-                    break
-                res = generate_circuits_on_all_levels(*res_qc_creation)
-                if not res:
-                    break
-
         elif benchmark["name"] == "pricingcall":
             for nodes in range(
                 benchmark["min_uncertainty"], benchmark["max_uncertainty"]
@@ -237,7 +232,6 @@ def generate_benchmark(benchmark):
                     break
                 res = generate_circuits_on_all_levels(*res_qc_creation)
                 if not res:
-                    print("####### FAIL")
                     break
 
     return
@@ -459,24 +453,6 @@ def create_groundstate_qc(choice: str):
         )
 
 
-def create_excitedstate_qc(choice: str):
-    molecule = utils.get_molecule(choice)
-
-    try:
-        qc = excitedstate.create_circuit(molecule)
-        qc.name = qc.name + "_" + choice
-        return qc, qc.num_qubits, False
-
-    except Exception as e:
-        print(
-            "\n Problem occured in outer loop: ",
-            "create_excitedstate_benchmarks",
-            choice,
-            e,
-        )
-        return False
-
-
 def create_pricingcall_qc(num_uncertainty: int):
     # num_options is not the number of qubits in this case
     try:
@@ -515,7 +491,7 @@ def get_one_benchmark(
     circuit_size: int = None,
     benchmark_instance_name: str = None,
     compiler: str = "qiskit",
-    compiler_settings: Union[str, int] = 0,
+    compiler_settings: dict[str, dict[str, any]] = None,
     gate_set_name: str = "ibm",
     device_name: str = "ibm_washington",
 ):
@@ -525,14 +501,14 @@ def get_one_benchmark(
     benchmark_name -- name of the to be generated benchmark
     level -- Choice of level, either as a string ("alg", "indep", "gates" or "mapped") or as a number between 0-3 where 0 corresponds to "alg" level and 3 to "mapped" leve
     circuit_size -- Input for the benchmark creation, in most cases this is equal to the qubit number
-    benchmark_instance_name -- Input selection for some benchmarks, namely "groundstate", "excitedstate" and "shor"
+    benchmark_instance_name -- Input selection for some benchmarks, namely "groundstate" and "shor"
     compiler -- "qiskit" or "tket"
-    compiler_settings -- Optimization level for if compiler is qiskit (0-3), Line Placement or Graph Placement if compiler is tket (True or False)
+    compiler_settings -- Dictionary containing the respective compiler settings for the specified compiler (e.g., optimization level for Qiskit or placement for TKET)
     gate_set_name -- "ibm", "rigetti", "ionq", or "oqc"
     device_name -- "ibm_washington", "ibm_montreal", "aspen_m1", "ionq11", ""lucy""
 
     Return values:
-    Quantum Circuit Object -- Representing the benchmark with the selected options, either as Qiskit::QuantumCircuit or Pytket::Circuit object
+    Quantum Circuit Object -- Representing the benchmark with the selected options, either as Qiskit::QuantumCircuit or Pytket::Circuit object (depending on the chosen compiler---while the algorithm level is always provided using Qiskit)
     """
     init_module_paths()
 
@@ -571,10 +547,6 @@ def get_one_benchmark(
         molecule = utils.get_molecule(benchmark_instance_name)
         qc = groundstate.create_circuit(molecule)
 
-    elif benchmark_name == "excitedstate":
-        molecule = utils.get_molecule(benchmark_instance_name)
-        qc = excitedstate.create_circuit(molecule)
-
     elif benchmark_name == "pricingcall":
         qc = pricingcall.create_circuit(circuit_size)
 
@@ -585,6 +557,12 @@ def get_one_benchmark(
         lib = importlib.import_module(benchmarks_module_paths_dict[benchmark_name])
         qc = lib.create_circuit(circuit_size)
 
+    if compiler_settings == None:
+        compiler_settings = {
+            "qiskit": {"optimization_level": 1},
+            "tket": {"placement": "lineplacement"},
+        }
+
     if level == "alg" or level == 0:
         return qc
 
@@ -600,8 +578,9 @@ def get_one_benchmark(
     elif level == "nativegates" or level == 2:
 
         if compiler == "qiskit":
+            opt_level = compiler_settings["qiskit"]["optimization_level"]
             qc_gates = qiskit_helper.get_native_gates_level(
-                qc, gate_set_name, circuit_size, compiler_settings, False, True
+                qc, gate_set_name, circuit_size, opt_level, False, True
             )
         elif compiler == "tket":
             qc_gates = tket_helper.get_native_gates_level(
@@ -612,22 +591,25 @@ def get_one_benchmark(
 
     elif level == "mapped" or level == 3:
         if compiler == "qiskit":
+            opt_level = compiler_settings["qiskit"]["optimization_level"]
             qc_mapped = qiskit_helper.get_mapped_level(
                 qc,
                 gate_set_name,
                 circuit_size,
                 device_name,
-                compiler_settings,
+                opt_level,
                 False,
                 True,
             )
         elif compiler == "tket":
+            placement = compiler_settings["tket"]["placement"].lower()
+            lineplacement = placement == "lineplacement"
             qc_mapped = tket_helper.get_mapped_level(
                 qc,
                 gate_set_name,
                 circuit_size,
                 device_name,
-                compiler_settings,
+                lineplacement,
                 False,
                 True,
             )
@@ -646,7 +628,6 @@ def get_one_benchmark(
 
     args = parser.parse_args()
 
-    print(args.file_name)
     print("#### Start generating")
     create_benchmarks_from_config(args.file_name)
     print("#### Start preprocessing")
diff --git a/mqt/bench/benchmarks/qiskit_application_nature/excitedstate.py b/mqt/bench/benchmarks/qiskit_application_nature/excitedstate.py
deleted file mode 100644
index e5685ae91..000000000
--- a/mqt/bench/benchmarks/qiskit_application_nature/excitedstate.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Code based on https://qiskit.org/documentation/nature/tutorials/04_excited_states_solvers.html
-
-
-from qiskit import Aer
-from qiskit.utils import QuantumInstance
-from qiskit.circuit.library import TwoLocal
-from qiskit.algorithms import VQE
-from qiskit.algorithms.optimizers import SLSQP, COBYLA
-
-
-def create_circuit(molecule, basis: str = "sto3g"):
-    """Returns a quantum circuit implementing Excited State Estimation.
-
-    Keyword arguments:
-    molecule -- molecule for which the excited state shall be estimated.
-    basis -- basis used for estimation
-    """
-    try:
-        from qiskit_nature.drivers import Molecule
-        from qiskit_nature.drivers.second_quantization import (
-            ElectronicStructureDriverType,
-            ElectronicStructureMoleculeDriver,
-        )
-        from qiskit_nature.problems.second_quantization import (
-            ElectronicStructureProblem,
-        )
-        from qiskit_nature.converters.second_quantization import QubitConverter
-        from qiskit_nature.mappers.second_quantization import JordanWignerMapper
-        from qiskit_nature.algorithms import GroundStateEigensolver, QEOM
-    except:
-        print("Please install qiskit_nature.")
-        return None
-
-    driver = ElectronicStructureMoleculeDriver(
-        molecule, basis=basis, driver_type=ElectronicStructureDriverType.PYSCF
-    )
-
-    es_problem = ElectronicStructureProblem(driver)
-    qubit_converter = QubitConverter(JordanWignerMapper())
-
-    # This first part sets the ground state solver
-    # see more about this part in the ground state calculation tutorial
-    # quantum_instance = QuantumInstance(Aer.get_backend("aer_simulator_statevector"))
-    # solver = VQEUCCFactory(quantum_instance)
-
-    tl_circuit = TwoLocal(
-        rotation_blocks=["h", "rx"],
-        entanglement_blocks="cz",
-        entanglement="full",
-        reps=2,
-        parameter_prefix="y",
-    )
-
-    another_solver = VQE(
-        ansatz=tl_circuit,
-        quantum_instance=QuantumInstance(Aer.get_backend("aer_simulator"), shots=1024),
-        optimizer=COBYLA(maxiter=25),
-    )
-    gsc = GroundStateEigensolver(qubit_converter, another_solver)
-
-    # The qEOM algorithm is simply instantiated with the chosen ground state solver
-    qeom_excited_states_calculation = QEOM(gsc, "sd")
-
-    qeom_results = qeom_excited_states_calculation.solve(es_problem)
-
-    qc = another_solver.get_optimal_circuit()
-    qc.name = "excitedstate"
-    qc.measure_all()
-
-    # print(qeom_results)
-    return qc
diff --git a/mqt/bench/utils/qiskit_helper.py b/mqt/bench/utils/qiskit_helper.py
index e1eb73fa9..27eeaf02f 100644
--- a/mqt/bench/utils/qiskit_helper.py
+++ b/mqt/bench/utils/qiskit_helper.py
@@ -60,7 +60,6 @@ def get_indep_level(
     qasm_output_folder = utils.get_qasm_output_path()
     filepath = qasm_output_folder + filename_indep + ".qasm"
     if not (path.isfile(filepath) and file_precheck):
-        # print(filepath + " does not already exists and is newly created")
         openqasm_gates = utils.get_openqasm_gates()
         target_independent = transpile(
             qc, basis_gates=openqasm_gates, optimization_level=1, seed_transpiler=10
@@ -108,12 +107,7 @@ def get_native_gates_level(
         path.isfile(qasm_output_folder + filename_nativegates + ".qasm")
         and file_precheck
     ):
-        # print(
-        #     qasm_output_folder
-        #     + filename_nativegates
-        #     + ".qasm"
-        #     + " does not already exists and is newly created"
-        # )
+
         compiled_without_architecture = transpile(
             qc, basis_gates=gate_set, optimization_level=opt_level, seed_transpiler=10
         )
@@ -183,12 +177,7 @@ def get_mapped_level(
     if not (
         path.isfile(qasm_output_folder + filename_mapped + ".qasm") and file_precheck
     ):
-        # print(
-        #     qasm_output_folder
-        #     + filename_mapped
-        #     + ".qasm"
-        #     + " does not already exists and is newly created"
-        # )
+
         compiled_with_architecture = transpile(
             qc,
             optimization_level=opt_level,
diff --git a/mqt/bench/utils/tket_helper.py b/mqt/bench/utils/tket_helper.py
index 62bfbf170..f1b8bd385 100644
--- a/mqt/bench/utils/tket_helper.py
+++ b/mqt/bench/utils/tket_helper.py
@@ -27,65 +27,7 @@ def get_rebase(gate_set_name: str, get_gatenames: bool = False):
         return get_ibm_rebase(get_gatenames)
     elif gate_set_name == "rigetti":
         return get_rigetti_rebase(get_gatenames)
-    #elif gate_set_name == "openqasm":
-        #return get_openqasm_rebase(get_gatenames)
-
-#
-# def get_openqasm_rebase(get_gatenames: bool = False):
-#     if get_gatenames:
-#         return utils.get_openqasm_gates()
-#     else:
-#         openqasm_rebase = auto_rebase_pass(
-#             {
-#                 OpType.U3,
-#                 OpType.U2,
-#                 OpType.U1,
-#                 OpType.CX,
-#                 OpType.noop,
-#                 # "u0",
-#                 # u",
-#                 # "p",
-#                 OpType.X,
-#                 OpType.Y,
-#                 OpType.Z,
-#                 OpType.H,
-#                 OpType.S,
-#                 OpType.Sdg,
-#                 OpType.T,
-#                 OpType.Tdg,
-#                 OpType.Rx,
-#                 OpType.Ry,
-#                 OpType.Rz,
-#                 OpType.SX,
-#                 OpType.SXdg,
-#                 OpType.CX,
-#                 OpType.CZ,
-#                 OpType.CY,
-#                 OpType.SWAP,
-#                 OpType.CH,
-#                 OpType.CCX,
-#                 OpType.CSWAP,
-#                 OpType.CRx,
-#                 OpType.CRy,
-#                 OpType.CRz,
-#                 OpType.CU1,
-#                 # OpType.CPhase,
-#                 OpType.CU3,
-#                 OpType.CSX,
-#                 # "cu",
-#                 OpType.XXPhase,
-#                 OpType.ZZPhase,
-#                 # "rccx",
-#                 # "rc3x",
-#                 # "c3x",
-#                 # "c3sqrtx",
-#                 # "c4x",
-#                 OpType.Measure,
-#             }
-#         )
-#
-#         return openqasm_rebase
-#
+
 
 def get_ionq_rebase(get_gatenames: bool = False):
     if get_gatenames:
@@ -140,12 +82,6 @@ def get_indep_level(
     if not (
         path.isfile(qasm_output_folder + filename_native + ".qasm") and file_precheck
     ):
-        # print(
-        #     qasm_output_folder
-        #     + filename_native
-        #     + ".qasm"
-        #     + " does not already exists and is newly created"
-        # )
         try:
             gates = list(set(utils.get_openqasm_gates()) - set(["rccx"]))
             qc = transpile(
@@ -186,13 +122,6 @@ def get_native_gates_level(
     if not (
         path.isfile(qasm_output_folder + filename_native + ".qasm") and file_precheck
     ):
-        # print(
-        #     qasm_output_folder
-        #     + filename_native
-        #     + ".qasm"
-        #     + " does not already exists and is newly created"
-        # )
-
         try:
             gates = list(set(utils.get_openqasm_gates()) - set(["rccx"]))
             qc = transpile(
@@ -248,12 +177,6 @@ def get_mapped_level(
         path.isfile(qasm_output_folder + filename_mapped + ".qasm") and file_precheck
     ):
         cmap = utils.get_cmap_from_devicename(device)
-        # print(
-        #     qasm_output_folder
-        #     + filename_mapped
-        #     + ".qasm"
-        #     + " does not already exists and is newly created"
-        # )
         try:
             gates = list(set(utils.get_openqasm_gates()) - set(["rccx"]))
             qc = transpile(
diff --git a/mqt/bench/utils/utils.py b/mqt/bench/utils/utils.py
index 979187f55..3990c18be 100644
--- a/mqt/bench/utils/utils.py
+++ b/mqt/bench/utils/utils.py
@@ -12,16 +12,17 @@
 import networkx as nx
 import numpy as np
 import os
+import subprocess
 
 from qiskit.test.mock import (
     FakeMontreal,
     FakeWashington,
 )
 
-qasm_path = "./benchviewer/static/files/qasm_output/"
+qasm_path = "mqt/benchviewer/static/files/qasm_output/"
 
 
-def set_qasm_output_path(new_path: str = "./benchviewer/static/files/qasm_output/"):
+def set_qasm_output_path(new_path: str = "mqt/benchviewer/static/files/qasm_output/"):
     global qasm_path
     qasm_path = new_path
 
@@ -355,14 +356,7 @@ def postprocess_ocr_qasm_files():
 
 
 def create_zip_file():
-    zf = ZipFile("MQTBench_all.zip", "w")
-    for dirname, subdirs, files in os.walk(get_qasm_output_path()):
-        for filename in files:
-            if filename.endswith(".qasm"):
-                zf.write(
-                    os.path.join(dirname, filename),
-                    compress_type=ZIP_LZMA,
-                    compresslevel=3,
-                    arcname=filename,
-                )
-    zf.close()
+    return subprocess.call(
+        "zip -rj ./mqt/benchviewer/static/files/MQTBench_all.zip ./mqt/benchviewer/static/files/qasm_output/",
+        shell=True,
+    )
diff --git a/mqt/benchviewer/__init__.py b/mqt/benchviewer/__init__.py
new file mode 100644
index 000000000..f7805e687
--- /dev/null
+++ b/mqt/benchviewer/__init__.py
@@ -0,0 +1,1 @@
+from mqt.benchviewer.main import start_server
diff --git a/benchviewer/main.py b/mqt/benchviewer/main.py
similarity index 51%
rename from benchviewer/main.py
rename to mqt/benchviewer/main.py
index e5b930622..522e76839 100644
--- a/benchviewer/main.py
+++ b/mqt/benchviewer/main.py
@@ -1,29 +1,41 @@
-import os.path
-import json
-
-from flask import (
-    Flask,
-    jsonify,
-    render_template,
-    request,
-    send_from_directory,
-)
-from src.backend import *
+from flask import Flask, jsonify, render_template, request, send_from_directory, cli
+from mqt.benchviewer.src.backend import *
 from datetime import datetime
 import logging
+import sys
 
+app = Flask(__name__)
+PREFIX = "/mqtbench/"
 
-def init():
-    read_mqtbench_all_zip()
-    init_database()
 
-    # logging.basicConfig(filename="/local/mqtbench/downloads.log", level=logging.INFO)
+def init(
+    skip_question: bool = False,
+    activate_logging: bool = False,
+    target_location: str = None,
+):
+    global TARGET_LOCATION
+    TARGET_LOCATION = target_location
+    if not os.access(TARGET_LOCATION, os.W_OK):
+        print("target_location is not writable. Please specify a different path.")
+        return False
 
+    res_zip = read_mqtbench_all_zip(skip_question, target_location)
+    if not res_zip:
+        return False
 
-init()
-app = Flask(__name__)
+    res_db = init_database()
+    if not res_db:
+        return False
 
-PREFIX = "/mqtbench/"
+    global ACTIVATE_LOGGING
+    ACTIVATE_LOGGING = activate_logging
+
+    if ACTIVATE_LOGGING:
+        logging.basicConfig(
+            filename="/local/mqtbench/downloads.log", level=logging.INFO
+        )
+
+    return True
 
 
 @app.route(f"{PREFIX}/", methods=["POST", "GET"])
@@ -40,12 +52,18 @@ def index():
 
 @app.route(f"{PREFIX}/get_pre_gen", methods=["POST", "GET"])
 def download_pre_gen_zip():
-    directory = "./static/files/qasm_output/"
     filename = "MQTBench_all.zip"
-    timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
+
+    if ACTIVATE_LOGGING:
+        timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
+        app.logger.info("###### Start ######")
+        app.logger.info("Timestamp: %s", timestamp)
+        app.logger.info("Headers: %s", request.headers)
+        app.logger.info("Download of pre-generated zip")
+        app.logger.info("###### End ######")
 
     return send_from_directory(
-        directory=directory,
+        directory=TARGET_LOCATION,
         path=filename,
         as_attachment=True,
         mimetype="application/zip",
@@ -59,11 +77,17 @@ def download_data():
     if request.method == "POST":
         data = request.form
         prepared_data = prepareFormInput(data)
-        print("raw data :", data)
-        print("prepared input data :", prepared_data)
         file_paths = get_selected_file_paths(prepared_data)
         timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
 
+        if ACTIVATE_LOGGING:
+            app.logger.info("###### Start ######")
+            app.logger.info("Timestamp: %s", timestamp)
+            app.logger.info("Headers: %s", request.headers)
+            app.logger.info("Prepared_data: %s", prepared_data)
+            app.logger.info("Download started: %s", len(file_paths))
+            app.logger.info("###### End ######")
+
         if file_paths:
             return app.response_class(
                 generate_zip_ephemeral_chunks(file_paths),
@@ -114,7 +138,6 @@ def get_num_benchmarks():
         prepared_data = prepareFormInput(data)
         file_paths = get_selected_file_paths(prepared_data)
         num = len(file_paths)
-        print("Num benchmarks: ", num)
         data = {"num_selected": num}
         return jsonify(data)
     else:
@@ -123,9 +146,39 @@ def get_num_benchmarks():
         return jsonify(data)
 
 
-def main():
-    app.run(debug=True)
+def start_server(
+    skip_question: bool = False,
+    activate_logging: bool = False,
+    target_location: str = None,
+    debug_flag: bool = False,
+):
+
+    if not target_location:
+        if sys.version_info < (3, 10, 0):
+            import importlib_resources as resources
+        else:
+            from importlib import resources
+        target_location = str(resources.files("mqt.benchviewer") / "static" / "files")
+
+    init(
+        skip_question=skip_question,
+        activate_logging=activate_logging,
+        target_location=target_location,
+    )
+    print(
+        "Server is hosted at: " + "http://127.0.0.1:5000" + PREFIX + ".",
+        "To stop it, interrupt the process (e.g., via CTRL+C). \n",
+    )
+
+    # This line avoid the startup-message from flask
+    cli.show_server_banner = lambda *args: None
+
+    if not activate_logging:
+        log = logging.getLogger("werkzeug")
+        log.disabled = True
+
+    app.run(debug=debug_flag)
 
 
 if __name__ == "__main__":
-    main()
+    start_server()
diff --git a/benchviewer/__init__.py b/mqt/benchviewer/src/__init__.py
similarity index 100%
rename from benchviewer/__init__.py
rename to mqt/benchviewer/src/__init__.py
diff --git a/benchviewer/src/backend.py b/mqt/benchviewer/src/backend.py
similarity index 76%
rename from benchviewer/src/backend.py
rename to mqt/benchviewer/src/backend.py
index e909f7de0..727dc032d 100644
--- a/benchviewer/src/backend.py
+++ b/mqt/benchviewer/src/backend.py
@@ -1,11 +1,21 @@
-import pandas as pd
+import io
 import os
 import re
-import json
-import io
-from zipfile import ZipFile, ZIP_DEFLATED
 from pathlib import Path
 from typing import List, Iterable
+from zipfile import ZipFile, ZIP_DEFLATED
+
+import pandas as pd
+import requests
+import sys
+from tqdm import tqdm
+from packaging import version
+
+if sys.version_info < (3, 10, 0):
+    import importlib_metadata as metadata
+else:
+    from importlib import metadata
+
 
 # All available benchmarks shown on our webpage are defined here
 benchmarks = [
@@ -61,14 +71,13 @@
 ]
 
 nonscalable_benchmarks = [
-    {"name": "Excited State", "id": "22", "filename": "excitedstate"},
-    {"name": "Ground State", "id": "23", "filename": "groundstate"},
-    {"name": "HHL", "id": "24", "filename": "hhl"},
-    {"name": "Pricing Call Option", "id": "25", "filename": "pricingcall"},
-    {"name": "Pricing Put Option", "id": "26", "filename": "pricingput"},
-    {"name": "Routing", "id": "27", "filename": "routing"},
-    {"name": "Shor's", "id": "28", "filename": "shor"},
-    {"name": "Travelling Salesman", "id": "29", "filename": "tsp"},
+    {"name": "Ground State", "id": "22", "filename": "groundstate"},
+    {"name": "HHL", "id": "23", "filename": "hhl"},
+    {"name": "Pricing Call Option", "id": "24", "filename": "pricingcall"},
+    {"name": "Pricing Put Option", "id": "25", "filename": "pricingput"},
+    {"name": "Routing", "id": "26", "filename": "routing"},
+    {"name": "Shor's", "id": "27", "filename": "shor"},
+    {"name": "Travelling Salesman", "id": "28", "filename": "tsp"},
 ]
 
 # Store the pandas dataframe as the database containing all available benchmarks
@@ -150,14 +159,115 @@ def createDatabase(zip_file: ZipFile):
     return database
 
 
-def read_mqtbench_all_zip():
+def read_mqtbench_all_zip(
+    skip_question: bool = False,
+    target_location: str = None,
+):
     global MQTBENCH_ALL_ZIP
-    huge_zip = Path("./static/files/qasm_output/MQTBench_all.zip")
-    print("Reading in {} ({} bytes) ...".format(huge_zip.name, huge_zip.stat().st_size))
-    with huge_zip.open("rb") as zf:
+    huge_zip_path = Path(target_location + "/MQTBench_all.zip")
+
+    try:
+        mqtbench_module_version = metadata.version("mqt.bench")
+    except Exception as e:
+        print(
+            "'mqt.bench' is most likely not installed. Please run 'pip install . or pip install mqt.bench'."
+        )
+        return False
+
+    print("Searching for local benchmarks...")
+    if (
+        os.path.isfile(huge_zip_path)
+        and len(ZipFile(huge_zip_path, "r").namelist()) != 0
+    ):
+        print("... found.")
+    else:
+        print("No benchmarks found. Querying GitHub...")
+
+        version_found = False
+        response = requests.get("https://api.github.com/repos/cda-tum/mqtbench/tags")
+        available_versions = []
+        for elem in response.json():
+            available_versions.append(elem["name"])
+
+        for possible_version in available_versions:
+            if version.parse(mqtbench_module_version) >= version.parse(
+                possible_version
+            ):
+                url = (
+                    "https://api.github.com/repos/cda-tum/mqtbench/releases/tags/"
+                    + possible_version
+                )
+
+                response = requests.get(url)
+                if not response:
+                    print(
+                        "Suitable benchmarks cannot be downloaded since the GitHub API failed. "
+                        "One reasons could be that the limit of 60 API calls per hour and IP address is exceeded."
+                    )
+                    return False
+
+                response_json = response.json()
+                if "assets" in response_json:
+                    assets = response_json["assets"]
+                elif "asset" in response_json:
+                    assets = [response_json["asset"]]
+                else:
+                    assets = []
+
+                for asset in assets:
+                    if asset["name"] == "MQTBench_all.zip":
+                        version_found = True
+
+                    if version_found:
+                        download_url = asset["browser_download_url"]
+                        if not skip_question:
+                            file_size = round((asset["size"]) / 2**20, 2)
+                            print(
+                                "Found 'MQTBench_all.zip' (Version {}, Size {} MB, Link: {})".format(
+                                    possible_version,
+                                    file_size,
+                                    download_url,
+                                )
+                            )
+                            response = input(
+                                "Would you like to downloaded the file? (Y/n)"
+                            )
+                        if skip_question or response.lower() == "y" or response == "":
+                            handle_downloading_benchmarks(target_location, download_url)
+                            break
+
+        if not version_found:
+            print("No suitable benchmarks found.")
+            return False
+
+    with huge_zip_path.open("rb") as zf:
         bytes = io.BytesIO(zf.read())
         MQTBENCH_ALL_ZIP = ZipFile(bytes, mode="r")
-    print("files: {}".format(len(MQTBENCH_ALL_ZIP.namelist())))
+    return True
+
+
+def handle_downloading_benchmarks(target_location: str, download_url: str):
+    print("Start downloading benchmarks...")
+
+    r = requests.get(download_url)
+    total_length = r.headers.get("content-length")
+    total_length = int(total_length)
+    fname = target_location + "/MQTBench_all.zip"
+
+    if not os.path.isdir(target_location):
+        os.makedirs(target_location)
+
+    with open(fname, "wb") as f, tqdm(
+        desc=fname,
+        total=total_length,
+        unit="iB",
+        unit_scale=True,
+        unit_divisor=1024,
+    ) as bar:
+        for data in r.iter_content(chunk_size=1024):
+            size = f.write(data)
+            bar.update(size)
+    print("Download completed to {}. Server is starting now.".format(fname))
 
 
 def get_tket_settings(filename: str):
@@ -232,7 +342,7 @@ def parse_data(filename: str):
     return parsed_data
 
 
-def filterDatabase(filterCriteria, database):
+def filterDatabase(filterCriteria: tuple, database: pd.DataFrame):
     """Filters the database according to the filter criteria.
 
     Keyword arguments:
@@ -404,8 +514,6 @@ def generate_zip_ephemeral_chunks(
     fileobj = NoSeekBytesIO(io.BytesIO())
 
     paths: List[Path] = [Path(name) for name in filenames]
-    # if python_files_list:
-    # paths.append(Path("./static/files/algo_level.txt"))
 
     with ZipFile(fileobj, mode="w") as zf:
         for individualFile in paths:
@@ -430,7 +538,7 @@ def generate_zip_ephemeral_chunks(
     fileobj.close()
 
 
-def get_selected_file_paths(prepared_data):
+def get_selected_file_paths(prepared_data: tuple):
     """Extracts all file paths according to the prepared user's filter criteria.
 
     Keyword arguments:
@@ -453,13 +561,18 @@ def init_database():
 
     assert MQTBENCH_ALL_ZIP is not None
 
-    print("Creating data base...")
+    print("Initiating database...")
     database = createDatabase(MQTBENCH_ALL_ZIP)
-    print("rows: {}".format(len(database)))
-    print("... done")
+    print("... done: {} benchmarks.".format(len(database)))
 
+    if not database.empty:
+        return True
+    else:
+        print("Database initialization failed.")
+        return False
 
-def prepareFormInput(formData):
+
+def prepareFormInput(formData: list):
     """Formats the formData extracted from the user's inputs."""
 
     min_qubits = -1
@@ -544,16 +657,6 @@ def prepareFormInput(formData):
         if "device_ionq_ionq11" in k:
             mapped_devices.append("ionq11")
 
-    # print("benchmarks: ", num_benchmarks)
-    # print("indep compiler: ", indep_qiskit_compiler, indep_tket_compiler)
-    # print("native compiler: ", nativegates_qiskit_compiler, nativegates_tket_compiler)
-    # print("native compiler settings qiskit: ", native_qiskit_opt_lvls)
-    # print("native compiler gatesets: ", native_gatesets)
-    # print("mapped compiler: ", mapped_qiskit_compiler, mapped_tket_compiler)
-    # print("mapped compiler settings qiskit: ", mapped_qiskit_opt_lvls)
-    # print("mapped compiler settings tket: ", mapped_tket_placements)
-    # print("mapped devices: ", mapped_devices)
-
     res = (
         (int(min_qubits), int(max_qubits)),
         num_benchmarks,
diff --git a/benchviewer/static/TUM_Logo_blau_rgb_s.svg b/mqt/benchviewer/static/TUM_Logo_blau_rgb_s.svg
similarity index 100%
rename from benchviewer/static/TUM_Logo_blau_rgb_s.svg
rename to mqt/benchviewer/static/TUM_Logo_blau_rgb_s.svg
diff --git a/benchviewer/static/favicon.ico b/mqt/benchviewer/static/favicon.ico
similarity index 100%
rename from benchviewer/static/favicon.ico
rename to mqt/benchviewer/static/favicon.ico
diff --git a/mqt/benchviewer/static/files/MQTBench_all.zip b/mqt/benchviewer/static/files/MQTBench_all.zip
new file mode 100644
index 000000000..15cb0ecb3
Binary files /dev/null and b/mqt/benchviewer/static/files/MQTBench_all.zip differ
diff --git a/benchviewer/templates/benchmark_description.html b/mqt/benchviewer/templates/benchmark_description.html
similarity index 83%
rename from benchviewer/templates/benchmark_description.html
rename to mqt/benchviewer/templates/benchmark_description.html
index 149c6d996..f112855ce 100644
--- a/benchviewer/templates/benchmark_description.html
+++ b/mqt/benchviewer/templates/benchmark_description.html
@@ -10,24 +10,18 @@
           integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
 
     <style>
-        div {
-            font-size: 0.8vw;
-        }
 
-        h1 {
-            font-size: 2.2vw;
-        }
     </style>
 
     <title>MQT Bench</title>
-    <link rel="shortcut icon" href="/app/mqtbench/benchviewer/static/favicon.ico">
+    <link rel="shortcut icon" href="../static/favicon.ico">
 </head>
 <body>
 <!-- Image and text -->
 <nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
     <a class="navbar-brand" href="/mqtbench">
 
-        <img src="/app/mqtbench/benchviewer/static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
+        <img src="../static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
              class="d-inline-block align-text-top">
         MQT Bench
     </a>
@@ -37,7 +31,7 @@
     </button>
     <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
         <div class="navbar-nav">
-            <a class="nav-item nav-link " href=".">Benchmarks</span></a>
+            <a class="nav-item nav-link " href="index">Benchmarks</span></a>
             <a class="nav-item nav-link " href="description ">Level and File Descriptions</a>
             <a class="nav-item nav-link active" href="benchmark_description ">Benchmark Description</a>
             <a class="nav-item nav-link " href="https://www.cda.cit.tum.de/research/quantum/" target="_blank">More on
@@ -49,7 +43,7 @@
 
 <div class="col-xs-12" style="height:80px;"></div>
 
-<div class="container-fluid">
+<div class="container">
     <h1>MQT Bench: Brief Description of Benchmarks</h1>
 
 
@@ -77,16 +71,6 @@ <h1>MQT Bench: Brief Description of Benchmarks</h1>
             </td>
             <td style="vertical-align:top">2 -> 130</td>
         </tr>
-        <tr>
-            <td style="vertical-align:top">Excited State</td>
-            <td style="vertical-align:top">This algorithms estimates the excited state of the H2
-                molecule ("small") using VQE with a TwoLocal ansatz.
-                The source code for the algorithmic level originates from
-                <a href="https://qiskit.org/documentation/nature/tutorials/04_excited_states_solvers.html"
-                   target="_blank">this page</a>.
-            </td>
-            <td style="vertical-align:top">4</td>
-        </tr>
         <tr>
             <td style="vertical-align:top">GHZ State</td>
             <td style="vertical-align:top">The Greenberger-Horne-Zeilinger state is an entangled quantum state with a
@@ -326,34 +310,6 @@ <h1>MQT Bench: Brief Description of Benchmarks</h1>
     <div class="col-xs-12" style="height:50px;"></div>
 
 
-    <!--            {"name": "GHZ State", "id": "1", "filename":"ghz"},
-                  {"name": "Graph State", "id": "2", "filename":"graphstate"},
-                  {"name": "W State", "id": "3", "filename":"wstate"},
-                  {"name": "Quantum Amplitude Estimation (QAE)", "id": "4", "filename":"ae"},
-                  {"name": "Quantum Fourier Transformation (QFT)", "id": "5", "filename":"qft"},
-                  {"name": "Entangled QFT", "id": "6", "filename":"qftentangled"},
-                  {"name": "Quantum Phase Estimation (QPE) exact", "id": "7", "filename":"qpeexact"},
-                  {"name": "Quantum Phase Estimation (QPE) inexact", "id": "8", "filename":"qpeinexact"},
-                  {"name": "Deutsch-Jozsa", "id": "9", "filename":"dj"},
-                  {"name": "Grover's (no ancilla)", "id": "10", "filename":"grover-noancilla"},
-                  {"name": "Grover's (v-chain)", "id": "11", "filename":"grover-vchain"},
-                  {"name": "Quantum Walk (no ancilla)", "id": "12", "filename":"qwalk-noancilla"},
-                  {"name": "Quantum Walk (v-chain", "id": "13", "filename":"qwalk-vchain"},
-                  {"name": "Quantum Approximation Optimization Algorithm (QAOA)", "id": "14", "filename":"qaoa"},
-                  {"name": "Variational Quantum Eigensolver (VQE)", "id": "15", "filename":"vqe"},
-                  {"name": "Portfolio Optimization with QAOA", "id": "16", "filename":"portfolioqaoa"},
-                  {"name": "Portfolio Optimization with VQE", "id": "17", "filename":"portfoliovqe"},
-                  {"name": "Ground State", "id": "18", "filename":"groundstate"},
-                  {"name": "Excited State", "id": "19", "filename":"excitedstate"},
-                  {"name": "Routing", "id": "20", "filename":"routing"},
-                  {"name": "Travelling Salesman", "id": "21", "filename":"tsp"},
-                  {"name": "Pricing Call Option", "id": "22", "filename":"pricingcall"},
-                  {"name": "Pricing Put Option", "id": "23", "filename":"pricingput"},
-                  {"name": "Quantum Generative Adversarial Network", "id": "23", "filename":"qgan"},
-                  {"name": "Shor's", "id": "24", "filename":"shor"},
-                  {"name": "HHL", "id": "25", "filename":"HHL"}]-->
-
-
     <!-- Optional JavaScript -->
     <!-- jQuery first, then Popper.js, then Bootstrap JS -->
     <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
diff --git a/benchviewer/templates/description.html b/mqt/benchviewer/templates/description.html
similarity index 85%
rename from benchviewer/templates/description.html
rename to mqt/benchviewer/templates/description.html
index 4567f1ec8..87a92759c 100644
--- a/benchviewer/templates/description.html
+++ b/mqt/benchviewer/templates/description.html
@@ -10,24 +10,18 @@
           integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
 
     <style>
-        div {
-            font-size: 0.8vw;
-        }
 
-        h1 {
-            font-size: 2.2vw;
-        }
     </style>
 
     <title>MQT Bench</title>
-    <link rel="shortcut icon" href="/app/mqtbench/benchviewer/static/favicon.ico">
+    <link rel="shortcut icon" href="../static/favicon.ico">
 </head>
 <body>
 <!-- Image and text -->
 <nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
     <a class="navbar-brand" href="/mqtbench">
 
-        <img src="/app/mqtbench/benchviewer/static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
+        <img src="../static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
              class="d-inline-block align-text-top">
         MQT Bench
     </a>
@@ -37,7 +31,7 @@
     </button>
     <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
         <div class="navbar-nav">
-            <a class="nav-item nav-link " href=".">Benchmarks</span></a>
+            <a class="nav-item nav-link " href="index">Benchmarks</span></a>
             <a class="nav-item nav-link active" href="description ">Level and File Descriptions</a>
             <a class="nav-item nav-link " href="benchmark_description ">Benchmark Description</a>
             <a class="nav-item nav-link " href="https://www.cda.cit.tum.de/research/quantum/" target="_blank">More on
@@ -48,7 +42,7 @@
 </nav>
 <div class="col-xs-12" style="height:80px;"></div>
 
-<div class="container-fluid">
+<div class="container">
     <h1>MQT Bench: Level Description and File Format</h1>
     <p>
         Benchmarks are provided on four different abstraction levels:
@@ -94,7 +88,7 @@ <h1>MQT Bench: Level Description and File Format</h1>
 
 
 
-<div class="container-fluid">
+<div class="container">
     <table class="table-sm">
         <thead>
         <tr>
@@ -115,18 +109,18 @@ <h1>MQT Bench: Level Description and File Format</h1>
         </tr>
         <tr>
             <td style="vertical-align:top">Target-independent Level</td>
-            <td style="vertical-align:top">&lt;benchmark_identifier&gt;_nativegates_&lt;number_of_qubits&gt;.qasm</td>
-            <td style="vertical-align:top">qft_t-indep_5.qasm</td>
+            <td style="vertical-align:top">&lt;benchmark_identifier&gt;_indep_&lt;compiler&gt;_&lt;number_of_qubits&gt;.qasm</td>
+            <td style="vertical-align:top">qft_indep_tket_5.qasm</td>
         </tr>
         <tr>
             <td style="vertical-align:top">Target-dependent Native Gates Level</td>
-            <td style="vertical-align:top">&lt;benchmark_identifier&gt;_nativegates_&lt;native_gate_set&gt;_&lt;optimization_level&gt;_&lt;number_of_qubits&gt;.qasm</td>
-            <td style="vertical-align:top">qft_nativegates_ibm_opt3_5.qasm</td>
+            <td style="vertical-align:top">&lt;benchmark_identifier&gt;_nativegates_&lt;native_gate_set&gt;_&lt;compiler&gt;_&lt;optional: compiler settings&gt;_&lt;number_of_qubits&gt;.qasm</td>
+            <td style="vertical-align:top">qft_nativegates_ibm_qiskit_opt3_5.qasm</td>
         </tr>
         <tr>
             <td style="vertical-align:top">Target-dependent Mapped Level</td>
-            <td style="vertical-align:top">benchmark_identifier&gt;_mapped_&lt;native_gate_set&gt;-&lt;mapping scheme&gt;_&lt;optimization_level&gt;_&lt;number_of_qubits&gt;.qasm</td>
-            <td style="vertical-align:top">qft_mapped_ibm-s_opt3_5.qasm</td>
+            <td style="vertical-align:top">&lt;benchmark_identifier&gt;_mapped_&lt;device&gt;_&lt;compiler&gt;_&lt;compiler settings>_&lt;number_of_qubits&gt;.qasm</td>
+            <td style="vertical-align:top">qft_nativegates_rigetti_aspen_m1_qiskit_opt3_5.qasm</td>
         </tr>
         </tbody>
     </table>
diff --git a/benchviewer/templates/index.html b/mqt/benchviewer/templates/index.html
similarity index 98%
rename from benchviewer/templates/index.html
rename to mqt/benchviewer/templates/index.html
index 1eef2afc7..268e5de46 100644
--- a/benchviewer/templates/index.html
+++ b/mqt/benchviewer/templates/index.html
@@ -14,14 +14,14 @@
     </style>
 
     <title>MQT Bench</title>
-    <link rel="shortcut icon" href="/app/mqtbench/benchviewer/static/favicon.ico">
+    <link rel="shortcut icon" href="../static/favicon.ico">
 </head>
 <body>
 <!-- Image and text -->
 <nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
     <a class="navbar-brand" href="/mqtbench">
 
-        <img src="/app/mqtbench/benchviewer/static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
+        <img src="../static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
              class="d-inline-block align-text-top">
         MQT Bench
     </a>
@@ -31,7 +31,7 @@
     </button>
     <div class="collapse navbar-collapse " id="navbarNavAltMarkup">
         <div class="navbar-nav">
-            <a class="nav-item nav-link active" href=".">Benchmarks</a>
+            <a class="nav-item nav-link active" href="index">Benchmarks</a>
             <a class="nav-item nav-link " href="description">Level and File Descriptions</a>
             <a class="nav-item nav-link " href="benchmark_description">Benchmark Description</a>
             <a class="nav-item nav-link " href="https://www.cda.cit.tum.de/research/quantum/" target="_blank">More on
diff --git a/benchviewer/templates/legal.html b/mqt/benchviewer/templates/legal.html
similarity index 94%
rename from benchviewer/templates/legal.html
rename to mqt/benchviewer/templates/legal.html
index 374c28247..39c4dca62 100644
--- a/benchviewer/templates/legal.html
+++ b/mqt/benchviewer/templates/legal.html
@@ -10,17 +10,11 @@
           integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
 
     <style>
-        div {
-            font-size: 0.8vw;
-        }
 
-        h1 {
-            font-size: 2.2vw;
-        }
     </style>
 
     <title>MQT Bench</title>
-    <link rel="shortcut icon" href="/app/mqtbench/benchviewer/static/favicon.ico">
+    <link rel="shortcut icon" href="../static/favicon.ico">
 </head>
 <body>
 
@@ -28,7 +22,7 @@
 <nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
     <a class="navbar-brand" href="/mqtbench">
 
-        <img src="/app/mqtbench/benchviewer/static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
+        <img src="../static/TUM_Logo_blau_rgb_s.svg" alt="" width="50" height="24"
              class="d-inline-block align-text-top">
         MQT Bench
     </a>
@@ -38,7 +32,7 @@
     </button>
     <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
         <div class="navbar-nav">
-            <a class="nav-item nav-link " href=".">Benchmarks</span></a>
+            <a class="nav-item nav-link " href="index">Benchmarks</span></a>
             <a class="nav-item nav-link " href="description ">Level and File Descriptions</span></a>
             <a class="nav-item nav-link " href="benchmark_description ">Benchmark Description</a>
             <a class="nav-item nav-link " href="https://www.cda.cit.tum.de/research/quantum/" target="_blank">More on
@@ -49,7 +43,7 @@
 </nav>
 
 <div class="col-xs-12" style="height:80px;"></div>
-<div class="container-fluid">
+<div class="container">
     <b>Herausgeber</b><br>
     Technische Universität München<br>
     Arcisstraße 21<br>
diff --git a/setup.py b/setup.py
index 3b54f528f..61438ef12 100644
--- a/setup.py
+++ b/setup.py
@@ -8,8 +8,13 @@
 setup(
     name="mqt.bench",
     packages=[
+        "mqt.benchviewer",
+        "mqt.benchviewer.src",
+        "mqt.benchviewer.src.tests",
+        "mqt.benchviewer.templates",
         "mqt.bench",
         "mqt.bench.utils",
+        "mqt.bench.tests",
         "mqt.bench.benchmarks",
         "mqt.bench.benchmarks.qiskit_application_finance",
         "mqt.bench.benchmarks.qiskit_application_ml",
@@ -26,21 +31,27 @@
     author_email="nils.quetschlich@tum.de",
     url="https://github.com/cda-tum/mqtbench",
     keywords="mqt quantum benchmarking performance testing",
+    entry_points={
+        "console_scripts": ["mqt.bench=mqt.benchviewer.main:start_server"],
+    },
+    include_package_data=True,
     install_requires=[
-        "qiskit[visualization]~=0.35.0",
-        "pandas~=1.3.5",
-        "flask~=2.1.2",
-        "networkx~=2.8.3",
-        "pytest~=7.1.1",
+        "qiskit==0.36.0",
+        "pytket==1.2.2",
+        "pytket-qiskit==0.25.0",
+        "pandas==1.3.5",
+        "flask==2.1.2",
+        "networkx==2.8.3",
+        "pytest==7.1.1",
+        "qiskit_finance==0.3.1",
+        "qiskit_machine-learning==0.4.0",
+        "qiskit-nature[pyscf]==0.3.1",
+        "qiskit_optimization==0.3.2",
+        "packaging==21.3",
+        "tqdm==4.64.0",
+        "importlib_metadata>=3.6; python_version < '3.10'",
+        "importlib_resources>=5.9; python_version < '3.10'",
     ],
-    extras_require={
-        "all": [
-            "qiskit_finance~=0.3.1",
-            "qiskit_machine-learning~=0.4.0",
-            "qiskit_nature~=0.3.1",
-            "qiskit_optimization~=0.3.2",
-        ],
-    },
     classifiers=[
         "Development Status :: 3 - Alpha",
         "Intended Audience :: Developers",


Test patch:
diff --git a/benchviewer/src/tests/__init__.py b/benchviewer/src/tests/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/mqt/bench/tests/test_all.py b/mqt/bench/tests/test_all.py
index babf02d7d..a9bbd18e9 100644
--- a/mqt/bench/tests/test_all.py
+++ b/mqt/bench/tests/test_all.py
@@ -26,7 +26,7 @@
     portfolioqaoa,
 )
 from mqt.bench.benchmarks.qiskit_application_ml import qgan
-from mqt.bench.benchmarks.qiskit_application_nature import groundstate, excitedstate
+from mqt.bench.benchmarks.qiskit_application_nature import groundstate
 from mqt.bench.benchmarks.qiskit_application_optimization import tsp, routing
 from qiskit_nature.drivers import Molecule
 from qiskit import QuantumCircuit
@@ -237,7 +237,7 @@ def test_dj_constant_oracle():
 
 
 @pytest.mark.skip(reason="Takes really long")
-def test_ground_and_excited_state():
+def test_groundstate():
     m_1 = Molecule(
         geometry=[["Li", [0.0, 0.0, 0.0]], ["H", [0.0, 0.0, 2.5]]],
         charge=0,
@@ -246,9 +246,6 @@ def test_ground_and_excited_state():
     qc = groundstate.create_circuit(m_1)
     assert qc.depth() > 0
 
-    qc = excitedstate.create_circuit(m_1)
-    assert qc.depth() > 0
-
 
 def test_routing():
     qc = routing.create_circuit(4, 2)
@@ -258,36 +255,314 @@ def test_routing():
 @pytest.mark.parametrize(
     "benchmark_name, level, circuit_size, benchmark_instance_name, compiler, compiler_settings, gate_set_name, device_name,",
     [
-        ("dj", "alg", 5, None, "qiskit", 1, None, None),
-        ("wstate", 0, 6, None, "qiskit", 2, None, None),
-        ("wstate", 0, 6, None, "tket", True, None, None),
-        ("wstate", 0, 6, None, "tket", False, None, None),
-        ("ghz", "indep", 5, None, "qiskit", 3, None, None),
-        ("graphstate", 1, 4, None, "qiskit", 0, None, None),
-        ("graphstate", 1, 4, None, "tket", True, None, None),
-        ("graphstate", 1, 4, None, "tket", False, None, None),
-        ("groundstate", 1, 4, "small", "qiskit", 1, None, None),
-        ("dj", "nativegates", 5, None, "qiskit", 2, "ionq", None),
-        ("dj", "nativegates", 5, None, "qiskit", 2, "ibm", None),
-        ("dj", "nativegates", 5, None, "qiskit", 2, "rigetti", None),
-        ("dj", "nativegates", 5, None, "qiskit", 2, "oqc", None),
-        ("qft", 2, 6, None, "qiskit", 3, "ionq", None),
-        ("qft", 2, 6, None, "qiskit", 3, "ibm", None),
-        ("qft", 2, 6, None, "tket", False, "rigetti", None),
-        ("qft", 2, 6, None, "tket", True, "oqc", None),
-        ("qpeexact", "mapped", 5, None, "qiskit", 1, "ibm", "ibm_washington"),
-        ("qpeexact", "mapped", 5, None, "qiskit", 1, "ibm", "ibm_montreal"),
-        ("qpeexact", "mapped", 5, None, "qiskit", 1, "rigetti", "rigetti_aspen_m1"),
-        ("qpeexact", "mapped", 5, None, "qiskit", 1, "ionq", "ionq11"),
-        ("qpeexact", "mapped", 5, None, "qiskit", 1, "oqc", "oqc_lucy"),
-        ("qpeinexact", 3, 4, None, "qiskit", 1, "ibm", "ibm_washington"),
-        ("qpeinexact", 3, 4, None, "tket", True, "ibm", "ibm_washington"),
-        ("qpeinexact", 3, 4, None, "qiskit", 1, "ibm", "ibm_montreal"),
-        ("qpeinexact", 3, 4, None, "tket", False, "ibm", "ibm_montreal"),
-        ("qpeinexact", 3, 4, None, "qiskit", 1, "rigetti", "rigetti_aspen_m1"),
-        ("qpeinexact", 3, 4, None, "tket", True, "rigetti", "rigetti_aspen_m1"),
-        ("qpeinexact", 3, 4, None, "qiskit", 1, "oqc", "oqc_lucy"),
-        ("qpeinexact", 3, 4, None, "tket", False, "oqc", "oqc_lucy"),
+        (
+            "dj",
+            "alg",
+            5,
+            None,
+            None,
+            None,
+            None,
+            None,
+        ),
+        (
+            "wstate",
+            0,
+            6,
+            None,
+            None,
+            None,
+            None,
+            None,
+        ),
+        (
+            "ghz",
+            "indep",
+            5,
+            None,
+            "qiskit",
+            None,
+            None,
+            None,
+        ),
+        (
+            "graphstate",
+            1,
+            4,
+            None,
+            "qiskit",
+            None,
+            None,
+            None,
+        ),
+        (
+            "graphstate",
+            1,
+            4,
+            None,
+            "tket",
+            None,
+            None,
+            None,
+        ),
+        (
+            "groundstate",
+            1,
+            4,
+            "small",
+            "qiskit",
+            None,
+            None,
+            None,
+        ),
+        (
+            "dj",
+            "nativegates",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 2},
+            },
+            "ionq",
+            None,
+        ),
+        (
+            "dj",
+            "nativegates",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 2},
+            },
+            "ibm",
+            None,
+        ),
+        (
+            "dj",
+            "nativegates",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 2},
+            },
+            "rigetti",
+            None,
+        ),
+        (
+            "dj",
+            "nativegates",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 2},
+            },
+            "oqc",
+            None,
+        ),
+        (
+            "qft",
+            2,
+            6,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 3},
+            },
+            "ionq",
+            None,
+        ),
+        (
+            "qft",
+            2,
+            6,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 3},
+            },
+            "ibm",
+            None,
+        ),
+        (
+            "qft",
+            2,
+            6,
+            None,
+            "tket",
+            None,
+            "rigetti",
+            None,
+        ),
+        (
+            "qft",
+            2,
+            6,
+            None,
+            "tket",
+            None,
+            "oqc",
+            None,
+        ),
+        (
+            "qpeexact",
+            "mapped",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "ibm",
+            "ibm_washington",
+        ),
+        (
+            "qpeexact",
+            "mapped",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "ibm",
+            "ibm_montreal",
+        ),
+        (
+            "qpeexact",
+            "mapped",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "rigetti",
+            "rigetti_aspen_m1",
+        ),
+        (
+            "qpeexact",
+            "mapped",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "ionq",
+            "ionq11",
+        ),
+        (
+            "qpeexact",
+            "mapped",
+            5,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "oqc",
+            "oqc_lucy",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "ibm",
+            "ibm_washington",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "tket",
+            {
+                "tket": {"placement": "lineplacement"},
+            },
+            "ibm",
+            "ibm_washington",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "ibm",
+            "ibm_montreal",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "tket",
+            {
+                "tket": {"placement": "graphplacement"},
+            },
+            "ibm",
+            "ibm_montreal",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "rigetti",
+            "rigetti_aspen_m1",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "tket",
+            {
+                "tket": {"placement": "lineplacement"},
+            },
+            "rigetti",
+            "rigetti_aspen_m1",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "qiskit",
+            {
+                "qiskit": {"optimization_level": 1},
+            },
+            "oqc",
+            "oqc_lucy",
+        ),
+        (
+            "qpeinexact",
+            3,
+            4,
+            None,
+            "tket",
+            {
+                "tket": {"placement": "graphplacement"},
+            },
+            "oqc",
+            "oqc_lucy",
+        ),
     ],
 )
 def test_get_one_benchmark(
@@ -329,22 +604,3 @@ def test_configure_end():
         os.remove(os.path.join(test_qasm_output_path, f))
     os.rmdir(test_qasm_output_path)
     utils.set_qasm_output_path()
-
-
-def test_generated_files():
-    directory = utils.get_qasm_output_path()
-
-    failed_files = []
-    for f in os.listdir(directory):
-        if f.endswith(".qasm"):
-            path = os.path.join(directory, f)
-            try:
-                qc = QuantumCircuit.from_qasm_file(path)
-            except:
-                failed_files.append(path)
-
-    with open("failed_files.txt", "w") as f:
-        for fail in failed_files:
-            f.write(str(fail) + "\n")
-
-    assert len(failed_files) == 0
diff --git a/benchviewer/src/__init__.py b/mqt/benchviewer/src/tests/__init__.py
similarity index 100%
rename from benchviewer/src/__init__.py
rename to mqt/benchviewer/src/tests/__init__.py
diff --git a/benchviewer/src/tests/test_backend.py b/mqt/benchviewer/src/tests/test_backend.py
similarity index 83%
rename from benchviewer/src/tests/test_backend.py
rename to mqt/benchviewer/src/tests/test_backend.py
index 10a4fbc86..6328fa64b 100644
--- a/benchviewer/src/tests/test_backend.py
+++ b/mqt/benchviewer/src/tests/test_backend.py
@@ -1,9 +1,11 @@
-from benchviewer.src import backend
+from mqt.benchviewer.src import backend
+from mqt.benchviewer.main import app, init
 
 import pytest
 from pathlib import Path
 import io
-from zipfile import ZipFile, ZIP_DEFLATED
+from zipfile import ZipFile
+import os
 
 
 @pytest.mark.parametrize(
@@ -145,14 +147,13 @@ def test_prepareFormInput():
             ("selectBench_19", "Real Amplitudes ansatz with Random Parameters"),
             ("selectBench_20", "Two Local ansatz with Random Parameters"),
             ("selectBench_21", "W-State"),
-            ("selectBench_22", "Excited State"),
-            ("selectBench_23", "Ground State"),
-            ("selectBench_24", "HHL"),
-            ("selectBench_25", "Pricing Call Option"),
-            ("selectBench_26", "Pricing Put Option"),
-            ("selectBench_27", "Routing"),
-            ("selectBench_28", "Shor's"),
-            ("selectBench_29", "Travelling Salesman"),
+            ("selectBench_22", "Ground State"),
+            ("selectBench_23", "HHL"),
+            ("selectBench_24", "Pricing Call Option"),
+            ("selectBench_25", "Pricing Put Option"),
+            ("selectBench_26", "Routing"),
+            ("selectBench_27", "Shor's"),
+            ("selectBench_28", "Travelling Salesman"),
             ("indep_qiskit_compiler", "true"),
             ("indep_tket_compiler", "true"),
             ("nativegates_qiskit_compiler", "true"),
@@ -212,7 +213,6 @@ def test_prepareFormInput():
             "26",
             "27",
             "28",
-            "29",
         ],
         (True, True),
         ((True, True), [0, 1, 2, 3], ["ibm", "rigetti", "oqc", "ionq"]),
@@ -232,9 +232,15 @@ def test_prepareFormInput():
     assert backend.prepareFormInput(form_data) == expected_res
 
 
-def test_create_database():
+def test_read_mqtbench_all_zip():
+    target_location = "mqt/benchviewer/static/files"
+    assert backend.read_mqtbench_all_zip(
+        skip_question=True, target_location=target_location
+    )
+
 
-    huge_zip = Path("./benchviewer/static/files/qasm_output/MQTBench_all.zip")
+def test_create_database():
+    huge_zip = Path("mqt/benchviewer/static/files/MQTBench_all.zip")
     MQTBENCH_ALL_ZIP = None
     with huge_zip.open("rb") as zf:
         bytes = io.BytesIO(zf.read())
@@ -255,14 +261,14 @@ def test_create_database():
     assert len(res) > 3
 
     input_data = (
-        (100, 110),
+        (110, 120),
         ["3"],
         (False, False),
         ((False, True), [], ["rigetti", "ionq"]),
         ((False, False), ([], []), []),
     )
     res = backend.get_selected_file_paths(input_data)
-    assert len(res) > 20
+    assert len(res) > 15
 
     input_data = (
         (75, 110),
@@ -276,7 +282,7 @@ def test_create_database():
 
     input_data = (
         (2, 5),
-        ["24"],
+        ["23"],
         (True, True),
         ((True, False), [1, 3], ["rigetti", "ionq", "oqc", "ibm"]),
         (
@@ -297,3 +303,26 @@ def test_create_database():
     )
     res = backend.get_selected_file_paths(input_data)
     assert res == []
+
+
+def test_flask_server():
+    assert init(
+        skip_question=True,
+        activate_logging=False,
+        target_location="mqt/benchviewer/static/files",
+    )
+
+    assert os.path.isfile(
+        os.path.join("mqt/benchviewer/static/files", "MQTBench_all.zip")
+    )
+    assert os.path.isfile("./mqt/benchviewer/templates/benchmark_description.html")
+    assert os.path.isfile("./mqt/benchviewer/templates/index.html")
+    assert os.path.isfile("./mqt/benchviewer/templates/legal.html")
+    assert os.path.isfile("./mqt/benchviewer/templates/description.html")
+
+    with app.test_client() as c:
+        assert c.get("/mqtbench/index").status_code == 200
+        assert c.get("/mqtbench/download").status_code == 200
+        assert c.get("/mqtbench/legal").status_code == 200
+        assert c.get("/mqtbench/description").status_code == 200
+        assert c.get("/mqtbench/benchmark_description").status_code == 200