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

Issue:
Provide Interfaces for MQT Predictor
The MQT Bench compilation pipelines should be made available such that 

- [ ] provided with a `qasm` file and a target location, the chosen mapped version is created, and
- [ ] there is an optional filename parameter to simplify finding the matching mapped circuits for the corresponding one on independent level.


Discussion / hints:

Fix patch:
diff --git a/config.json b/config.json
index 5758d36f7..a0904ebb5 100644
--- a/config.json
+++ b/config.json
@@ -1,5 +1,5 @@
 {
-"timeout": 100,
+"timeout": 1,
 "benchmarks": [
   {
     "name": "ae",
@@ -15,11 +15,6 @@
     "max_qubits": 130,
     "stepsize": 1
   },
-  {
-    "name": "excitedstate",
-    "include": true,
-    "instances": ["small"]
-  },
   {
     "name": "ghz",
     "include": true,
diff --git a/mqt/bench/utils/qiskit_helper.py b/mqt/bench/utils/qiskit_helper.py
index 27eeaf02f..0bd5887dc 100644
--- a/mqt/bench/utils/qiskit_helper.py
+++ b/mqt/bench/utils/qiskit_helper.py
@@ -42,6 +42,8 @@ def get_indep_level(
     num_qubits: int,
     file_precheck: bool,
     return_qc: bool = False,
+    target_directory: str = "",
+    target_filename: str = "",
 ):
     """Handles the creation of the benchmark on the target-independent level.
 
@@ -49,17 +51,25 @@ def get_indep_level(
     qc -- quantum circuit which the to be created benchmark circuit is based on
     num_qubits -- number of qubits
     file_precheck -- flag indicating whether to check whether the file already exists before creating it (again)
+    return_qc -- flag if the actual circuit shall be returned
+    target_directory -- alternative directory to the default one to store the created circuit
+    target_filename -- alternative filename to the default one
 
     Return values:
-    filename_indep -- the filename of the created and saved benchmark
-    depth -- circuit depth of created benchmark
-    num_qubits -- number of qubits of generated circuit
+    if return_qc == True -- quantum circuit object
+    else -- True/False indicating whether the function call was successful or not
     """
 
-    filename_indep = qc.name + "_indep_qiskit_" + str(num_qubits)
-    qasm_output_folder = utils.get_qasm_output_path()
-    filepath = qasm_output_folder + filename_indep + ".qasm"
-    if not (path.isfile(filepath) and file_precheck):
+    if not target_filename:
+        filename_indep = qc.name + "_indep_qiskit_" + str(num_qubits)
+        target_directory = utils.get_qasm_output_path()
+    else:
+        filename_indep = target_filename
+
+    if not (
+        path.isfile(path.join(target_directory, filename_indep) + ".qasm")
+        and file_precheck
+    ):
         openqasm_gates = utils.get_openqasm_gates()
         target_independent = transpile(
             qc, basis_gates=openqasm_gates, optimization_level=1, seed_transpiler=10
@@ -68,7 +78,11 @@ def get_indep_level(
         if return_qc:
             return target_independent
         else:
-            res = utils.save_as_qasm(target_independent.qasm(), filename_indep)
+            res = utils.save_as_qasm(
+                target_independent.qasm(),
+                filename_indep,
+                target_directory=target_directory,
+            )
             return res
     else:
         return True
@@ -81,30 +95,44 @@ def get_native_gates_level(
     opt_level: int,
     file_precheck: bool,
     return_qc: bool = False,
+    target_directory: str = "",
+    target_filename: str = "",
 ):
     """Handles the creation of the benchmark on the target-dependent native gates level.
 
     Keyword arguments:
     qc -- quantum circuit which the to be created benchmark circuit is based on
     gate_set_name -- name of this gate set
-    opt_level -- optimization level
     num_qubits -- number of qubits
+    opt_level -- optimization level
     file_precheck -- flag indicating whether to check whether the file already exists before creating it (again)
+    return_qc -- flag if the actual circuit shall be returned
+    target_directory -- alternative directory to the default one to store the created circuit
+    target_filename -- alternative filename to the default one
+
+    Return values:
+    if return_qc == True -- quantum circuit object
+    else -- True/False indicating whether the function call was successful or not
     """
 
-    filename_nativegates = (
-        qc.name
-        + "_nativegates_"
-        + gate_set_name
-        + "_qiskit_opt"
-        + str(opt_level)
-        + "_"
-        + str(num_qubits)
-    )
     gate_set = get_native_gates(gate_set_name)
-    qasm_output_folder = utils.get_qasm_output_path()
+
+    if not target_filename:
+        filename_native = (
+            qc.name
+            + "_nativegates_"
+            + gate_set_name
+            + "_qiskit_opt"
+            + str(opt_level)
+            + "_"
+            + str(num_qubits)
+        )
+        target_directory = utils.get_qasm_output_path()
+    else:
+        filename_native = target_filename
+
     if not (
-        path.isfile(qasm_output_folder + filename_nativegates + ".qasm")
+        path.isfile(path.join(target_directory, filename_native) + ".qasm")
         and file_precheck
     ):
 
@@ -129,6 +157,7 @@ def get_native_gates_level(
                 compiled_without_architecture.qasm(),
                 filename_nativegates,
                 gate_set,
+                target_directory=target_directory,
             )
             return res
     else:
@@ -143,6 +172,8 @@ def get_mapped_level(
     opt_level: int,
     file_precheck: bool,
     return_qc: bool = False,
+    target_directory: str = "",
+    target_filename: str = "",
 ):
     """Handles the creation of the benchmark on the target-dependent mapped level.
 
@@ -152,9 +183,10 @@ def get_mapped_level(
     num_qubits -- number of qubits
     device_name -- -- name of the target device
     opt_level -- optimization level
-    num_qubits -- number of qubits
     file_precheck -- flag indicating whether to check whether the file already exists before creating it (again)
-    return_qc -- flag indicating whether the actual circuit object is returned
+    return_qc -- flag if the actual circuit shall be returned
+    target_directory -- alternative directory to the default one to store the created circuit
+    target_filename -- alternative filename to the default one
 
     Return values:
     if return_qc == True -- quantum circuit object
@@ -164,18 +196,23 @@ def get_mapped_level(
     gate_set = get_native_gates(gate_set_name)
     c_map = utils.get_cmap_from_devicename(device_name)
 
-    filename_mapped = (
-        qc.name
-        + "_mapped_"
-        + device_name
-        + "_qiskit_opt"
-        + str(opt_level)
-        + "_"
-        + str(num_qubits)
-    )
-    qasm_output_folder = utils.get_qasm_output_path()
+    if not target_filename:
+        filename_mapped = (
+            qc.name
+            + "_mapped_"
+            + device_name
+            + "_qiskit_opt"
+            + str(opt_level)
+            + "_"
+            + str(num_qubits)
+        )
+        target_directory = utils.get_qasm_output_path()
+    else:
+        filename_mapped = target_filename
+
     if not (
-        path.isfile(qasm_output_folder + filename_mapped + ".qasm") and file_precheck
+        path.isfile(path.join(target_directory, target_filename) + ".qasm")
+        and file_precheck
     ):
 
         compiled_with_architecture = transpile(
@@ -194,6 +231,7 @@ def get_mapped_level(
                 gate_set,
                 True,
                 c_map,
+                target_directory,
             )
             return res
     else:
diff --git a/mqt/bench/utils/tket_helper.py b/mqt/bench/utils/tket_helper.py
index f1b8bd385..c67b0df44 100644
--- a/mqt/bench/utils/tket_helper.py
+++ b/mqt/bench/utils/tket_helper.py
@@ -74,14 +74,34 @@ def get_indep_level(
     num_qubits: int,
     file_precheck: bool,
     return_qc: bool = False,
+    target_directory: str = "",
+    target_filename: str = "",
 ):
-    qasm_output_folder = utils.get_qasm_output_path()
-
-    filename_native = qc.name + "_indep_tket_" + str(num_qubits)
+    """Handles the creation of the benchmark on the target-independent level.
+
+    Keyword arguments:
+    qc -- quantum circuit which the to be created benchmark circuit is based on
+    num_qubits -- number of qubits
+    file_precheck -- flag indicating whether to check whether the file already exists before creating it (again)
+    return_qc -- flag if the actual circuit shall be returned
+    target_directory -- alternative directory to the default one to store the created circuit
+    target_filename -- alternative filename to the default one
+
+    Return values:
+    if return_qc == True -- quantum circuit object
+    else -- True/False indicating whether the function call was successful or not
+    """
+    if not target_filename:
+        filename_indep = qc.name + "_indep_tket_" + str(num_qubits)
+        target_directory = utils.get_qasm_output_path()
+    else:
+        filename_indep = target_filename
 
     if not (
-        path.isfile(qasm_output_folder + filename_native + ".qasm") and file_precheck
+        path.isfile(path.join(target_directory, filename_indep) + ".qasm")
+        and file_precheck
     ):
+
         try:
             gates = list(set(utils.get_openqasm_gates()) - set(["rccx"]))
             qc = transpile(
@@ -94,14 +114,16 @@ def get_indep_level(
         except Exception as e:
             print("TKET Exception Indep: ", e)
             return False
-        # openqasm_gate_set_rebase = get_rebase("openqasm")
-        # openqasm_gate_set_rebase.apply(qc_tket)
         FullPeepholeOptimise().apply(qc_tket)
         if return_qc:
             return qc_tket
         else:
 
-            res = utils.save_as_qasm(circuit_to_qasm_str(qc_tket), filename_native)
+            res = utils.save_as_qasm(
+                circuit_to_qasm_str(qc_tket),
+                filename_indep,
+                target_directory=target_directory,
+            )
             return res
     else:
         return True
@@ -113,15 +135,38 @@ def get_native_gates_level(
     num_qubits: int,
     file_precheck: bool,
     return_qc: bool = False,
+    target_directory: str = "",
+    target_filename: str = "",
 ):
-    qasm_output_folder = utils.get_qasm_output_path()
+    """Handles the creation of the benchmark on the target-dependent native gates level.
+
+    Keyword arguments:
+    qc -- quantum circuit which the to be created benchmark circuit is based on
+    gate_set_name -- name of this gate set
+    num_qubits -- number of qubits
+    file_precheck -- flag indicating whether to check whether the file already exists before creating it (again)
+    return_qc -- flag if the actual circuit shall be returned
+    target_directory -- alternative directory to the default one to store the created circuit
+    target_filename -- alternative filename to the default one
+
+    Return values:
+    if return_qc == True -- quantum circuit object
+    else -- True/False indicating whether the function call was successful or not
+    """
+
+    if not target_filename:
+        filename_native = (
+            qc.name + "_nativegates_" + gate_set_name + "_tket_" + str(num_qubits)
+        )
+        target_directory = utils.get_qasm_output_path()
+    else:
+        filename_native = target_filename
 
-    filename_native = (
-        qc.name + "_nativegates_" + gate_set_name + "_tket_" + str(num_qubits)
-    )
     if not (
-        path.isfile(qasm_output_folder + filename_native + ".qasm") and file_precheck
+        path.isfile(path.join(target_directory, filename_native) + ".qasm")
+        and file_precheck
     ):
+
         try:
             gates = list(set(utils.get_openqasm_gates()) - set(["rccx"]))
             qc = transpile(
@@ -146,7 +191,10 @@ def get_native_gates_level(
             return qc_tket
         else:
             res = utils.save_as_qasm(
-                circuit_to_qasm_str(qc_tket), filename_native, native_gatenames
+                circuit_to_qasm_str(qc_tket),
+                filename_native,
+                native_gatenames,
+                target_directory=target_directory,
             )
             return res
     else:
@@ -157,26 +205,55 @@ def get_mapped_level(
     qc: circuit,
     gate_set_name: str,
     num_qubits: int,
-    device: str,
+    device_name: str,
     lineplacement: bool,
     file_precheck: bool,
     return_qc: bool = False,
+    target_directory: str = "",
+    target_filename: str = "",
 ):
-
-    qasm_output_folder = utils.get_qasm_output_path()
+    """Handles the creation of the benchmark on the target-dependent mapped level.
+
+    Keyword arguments:
+    qc -- quantum circuit which the to be created benchmark circuit is based on
+    gate_set_name -- name of the gate set
+    num_qubits -- number of qubits
+    device_name -- -- name of the target device
+    lineplacement -- if true line placement is used, else graph placement
+    file_precheck -- flag indicating whether to check whether the file already exists before creating it (again)
+    return_qc -- flag if the actual circuit shall be returned
+    target_directory -- alternative directory to the default one to store the created circuit
+    target_filename -- alternative filename to the default one
+
+    Return values:
+    if return_qc == True -- quantum circuit object
+    else -- True/False indicating whether the function call was successful or not
+    """
 
     if lineplacement:
         placement = "line"
     else:
         placement = "graph"
 
-    filename_mapped = (
-        qc.name + "_mapped_" + device + "_tket_" + placement + "_" + str(num_qubits)
-    )
+    if not target_filename:
+        filename_mapped = (
+            qc.name
+            + "_mapped_"
+            + device_name
+            + "_tket_"
+            + placement
+            + "_"
+            + str(num_qubits)
+        )
+        target_directory = utils.get_qasm_output_path()
+    else:
+        filename_mapped = target_filename
+
     if not (
-        path.isfile(qasm_output_folder + filename_mapped + ".qasm") and file_precheck
+        path.isfile(path.join(target_directory, filename_mapped) + ".qasm")
+        and file_precheck
     ):
-        cmap = utils.get_cmap_from_devicename(device)
+        cmap = utils.get_cmap_from_devicename(device_name)
         try:
             gates = list(set(utils.get_openqasm_gates()) - set(["rccx"]))
             qc = transpile(
@@ -212,6 +289,7 @@ def get_mapped_level(
                 native_gatenames,
                 True,
                 cmap,
+                target_directory,
             )
             return res
     else:
diff --git a/mqt/bench/utils/utils.py b/mqt/bench/utils/utils.py
index 3990c18be..f8f584a27 100644
--- a/mqt/bench/utils/utils.py
+++ b/mqt/bench/utils/utils.py
@@ -216,6 +216,7 @@ def save_as_qasm(
     gate_set: list = None,
     mapped: bool = False,
     c_map=None,
+    target_directory: str = "",
 ):
     """Saves a quantum circuit as a qasm file.
 
@@ -230,8 +231,13 @@ def save_as_qasm(
     if c_map is None:
         c_map = []
 
-    qasm_output_folder = get_qasm_output_path()
-    with open(qasm_output_folder + filename + ".qasm", "w") as f:
+    if target_directory:
+        qasm_output_folder = target_directory
+    else:
+        qasm_output_folder = get_qasm_output_path()
+
+    filename = os.path.join(qasm_output_folder, filename) + ".qasm"
+    with open(filename, "w") as f:
         f.write("// Benchmark was created by MQT Bench on " + str(date.today()) + "\n")
         f.write(
             "// For more information about MQT Bench, please visit https://www.cda.cit.tum.de/mqtbench/"


Test patch:
diff --git a/mqt/bench/tests/test_all.py b/mqt/bench/tests/test_all.py
index a9bbd18e9..e8e143823 100644
--- a/mqt/bench/tests/test_all.py
+++ b/mqt/bench/tests/test_all.py
@@ -236,14 +236,9 @@ def test_dj_constant_oracle():
     assert qc.depth() > 0
 
 
-@pytest.mark.skip(reason="Takes really long")
 def test_groundstate():
-    m_1 = Molecule(
-        geometry=[["Li", [0.0, 0.0, 0.0]], ["H", [0.0, 0.0, 2.5]]],
-        charge=0,
-        multiplicity=1,
-    )
-    qc = groundstate.create_circuit(m_1)
+    m = utils.get_molecule("small")
+    qc = groundstate.create_circuit(m)
     assert qc.depth() > 0
 
 
@@ -604,3 +599,39 @@ 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()
+
+
+@pytest.mark.parametrize(
+    "abstraction_level",
+    [
+        (1),
+        (2),
+        (3),
+    ],
+)
+def test_saving_qasm_to_alternative_location_with_alternative_filename(
+    abstraction_level: int,
+):
+    directory = "."
+    filename = "ae_test_qiskit"
+    qc = get_one_benchmark("ae", abstraction_level, 5)
+    assert qc
+    res = qiskit_helper.get_mapped_level(
+        qc, "ibm", qc.num_qubits, "ibm_washington", 1, False, False, directory, filename
+    )
+    assert res
+    path = os.path.join(directory, filename) + ".qasm"
+    assert os.path.isfile(path)
+    os.remove(path)
+
+    directory = "."
+    filename = "ae_test_tket"
+    qc = get_one_benchmark("ae", abstraction_level, 7)
+    assert qc
+    res = tket_helper.get_mapped_level(
+        qc, "ibm", qc.num_qubits, "ibm_washington", 1, False, False, directory, filename
+    )
+    assert res
+    path = os.path.join(directory, filename) + ".qasm"
+    assert os.path.isfile(path)
+    os.remove(path)