Repository: Qiskit/qiskit-ibm-runtime
Pull request #853 (2023-05-14 09:20:24)

Error message upon unsupported values for max_execution_time
<!--
⚠️ The pull request title should be short detailed and understandable for all.
⚠️ Also, please add a release note file using reno if the change needs to be
  documented in the release notes.
⚠️ If your pull request fixes an open issue, please link to the issue.

✅ I have added the tests to cover my changes.
✅ I have updated the documentation accordingly.
✅ I have read the CONTRIBUTING document.
-->

### Summary
Added error message when max_execution_time is outside the allowed range. Previously, this gave an error on the server side.


### Details and comments
Related to  #851.
Should actually have been part of #781.



coveralls: ## Pull Request Test Coverage Report for [Build 4972216002](https://coveralls.io/builds/59927568)

* **6** of **6**   **(100.0%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.04%**) to **67.217%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/59927568/badge)](https://coveralls.io/builds/59927568) |
| :-- | --: |
| Change from base [Build 4960335121](https://coveralls.io/builds/59904009): |  0.04% |
| Covered Lines: | 3420 |
| Relevant Lines: | 5088 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/qiskit_ibm_runtime/options/options.py b/qiskit_ibm_runtime/options/options.py
index 9c2259f5..8f7cfacc 100644
--- a/qiskit_ibm_runtime/options/options.py
+++ b/qiskit_ibm_runtime/options/options.py
@@ -89,6 +89,8 @@ class Options:
     _MAX_OPTIMIZATION_LEVEL = 3
     _MAX_RESILIENCE_LEVEL_ESTIMATOR = 3
     _MAX_RESILIENCE_LEVEL_SAMPLER = 1
+    _MIN_EXECUTION_TIME = 300
+    _MAX_EXECUTION_TIME = 8 * 60 * 60  # 8 hours for real device
 
     optimization_level: Optional[int] = None
     resilience_level: Optional[int] = None
@@ -164,7 +166,8 @@ class Options:
     def validate_options(options: dict) -> None:
         """Validate that program inputs (options) are valid
         Raises:
-            ValueError: if optimization_level is out of the allowed range.
+            ValueError: if optimization_level is outside the allowed range.
+            ValueError: if max_execution_time is outside the allowed range.
         """
         if not options.get("optimization_level") in list(
             range(Options._MAX_OPTIMIZATION_LEVEL + 1)
@@ -177,6 +180,17 @@ class Options:
         TranspilationOptions.validate_transpilation_options(
             options.get("transpilation")
         )
+        execution_time = options.get("max_execution_time")
+        if not execution_time is None:
+            if (
+                execution_time < Options._MIN_EXECUTION_TIME
+                or execution_time > Options._MAX_EXECUTION_TIME
+            ):
+                raise ValueError(
+                    f"max_execution_time must be between "
+                    f"{Options._MIN_EXECUTION_TIME} and {Options._MAX_EXECUTION_TIME} seconds."
+                )
+
         EnvironmentOptions.validate_environment_options(options.get("environment"))
 
     @staticmethod
diff --git a/releasenotes/notes/error_msg_max_execution_time-e401d353ae2a7d86.yaml b/releasenotes/notes/error_msg_max_execution_time-e401d353ae2a7d86.yaml
new file mode 100644
index 00000000..63423dd7
--- /dev/null
+++ b/releasenotes/notes/error_msg_max_execution_time-e401d353ae2a7d86.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    Added error messages in case the user defines unsupported values 
+    for 'max_execution_time'.
+    Previously, this validation was done on the server side.


Test patch:
diff --git a/test/unit/test_ibm_primitives.py b/test/unit/test_ibm_primitives.py
index e11821c5..a375f07f 100644
--- a/test/unit/test_ibm_primitives.py
+++ b/test/unit/test_ibm_primitives.py
@@ -680,6 +680,33 @@ class TestPrimitives(IBMTestCase):
                     list(opts_dict["transpilation"].keys())[0], str(exc.exception)
                 )
 
+    def test_max_execution_time_options(self):
+        """Test transpilation options."""
+        options_dicts = [
+            {"max_execution_time": Options._MIN_EXECUTION_TIME - 1},
+            {"max_execution_time": Options._MAX_EXECUTION_TIME + 1},
+        ]
+        session = MagicMock(spec=MockSession)
+        primitives = [Sampler, Estimator]
+
+        for cls in primitives:
+            for opts_dict in options_dicts:
+                # When this environment variable is set, validation is turned off
+                try:
+                    os.environ["QISKIT_RUNTIME_SKIP_OPTIONS_VALIDATION"] = "1"
+                    inst = cls(session=session, options=opts_dict)
+                    inst.run(self.qx, observables=self.obs)
+                finally:
+                    # Delete environment variable to validate input
+                    del os.environ["QISKIT_RUNTIME_SKIP_OPTIONS_VALIDATION"]
+                with self.assertRaises(ValueError) as exc:
+                    inst = cls(session=session, options=opts_dict)
+                    inst.run(self.qx, observables=self.obs)
+                self.assertIn(
+                    "max_execution_time must be between 300 and 28800 seconds",
+                    str(exc.exception),
+                )
+
     def test_raise_faulty_qubits(self):
         """Test faulty qubits is raised."""
         fake_backend = FakeManila()