From ecf6088d6003a9cc3852a36f99dc28a741a6ec0b Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 2 Dec 2024 23:27:27 -0800 Subject: [PATCH 1/4] Fix Qiksit interop with v1.3 Qiskit v1.3 changed the behavior of `num_qubits` on the `Target` class in way that broke our `QirTarget` usage, since we expect to default to `None` but the new logic ignores the `num_qubits` parameter to `__init__` and always defaults to zero qubits, failing later calls to transpilation. By shadowing the parent class property in `QirTarget` we can continue returning `None` and trigger the right behavior in transpilation while preserving a setter for use with v1.2. Fixes #2047 --- build.py | 43 ++++++++++++++----- .../interop/qiskit/backends/backend_base.py | 30 ++++++++----- .../interop/qiskit/backends/qirtarget.py | 15 +++++++ pip/tests-integration/test_requirements.txt | 2 +- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/build.py b/build.py index fbc552a84d..d654c0d38b 100755 --- a/build.py +++ b/build.py @@ -69,10 +69,10 @@ ) parser.add_argument( - "--ci-bench", - action=argparse.BooleanOptionalAction, - default=False, - help="Run the benchmarking script that is run in CI (default is --no-ci-bench)", + "--ci-bench", + action=argparse.BooleanOptionalAction, + default=False, + help="Run the benchmarking script that is run in CI (default is --no-ci-bench)", ) args = parser.parse_args() @@ -303,25 +303,46 @@ def run_python_integration_tests(cwd, interpreter): def run_ci_historic_benchmark(): branch = "main" output = subprocess.check_output( - ["git", "rev-list", "--since=1 week ago", "--pretty=format:%ad__%h", "--date=short", branch] + [ + "git", + "rev-list", + "--since=1 week ago", + "--pretty=format:%ad__%h", + "--date=short", + branch, + ] ).decode("utf-8") - print('\n'.join([line for i, line in enumerate(output.split('\n')) if i % 2 == 1])) + print("\n".join([line for i, line in enumerate(output.split("\n")) if i % 2 == 1])) output = subprocess.check_output( - ["git", "rev-list", "--since=1 week ago", "--pretty=format:%ad__%h", "--date=short", branch] + [ + "git", + "rev-list", + "--since=1 week ago", + "--pretty=format:%ad__%h", + "--date=short", + branch, + ] ).decode("utf-8") - date_and_commits = [line for i, line in enumerate(output.split('\n')) if i % 2 == 1] + date_and_commits = [line for i, line in enumerate(output.split("\n")) if i % 2 == 1] for date_and_commit in date_and_commits: print("benching commit", date_and_commit) result = subprocess.run( - ["cargo", "criterion", "--message-format=json", "--history-id", date_and_commit], + [ + "cargo", + "criterion", + "--message-format=json", + "--history-id", + date_and_commit, + ], capture_output=True, - text=True + text=True, ) with open(f"{date_and_commit}.json", "w") as f: f.write(result.stdout) + if build_pip: step_start("Building the pip package") @@ -514,7 +535,7 @@ def run_ci_historic_benchmark(): "ipykernel", "nbconvert", "pandas", - "qiskit>=1.2.2,<1.3.0", + "qiskit>=1.2.2,<2.0.0", ] subprocess.run(pip_install_args, check=True, text=True, cwd=root_dir, env=pip_env) diff --git a/pip/qsharp/interop/qiskit/backends/backend_base.py b/pip/qsharp/interop/qiskit/backends/backend_base.py index 5faf2c2557..a6b18d8ea2 100644 --- a/pip/qsharp/interop/qiskit/backends/backend_base.py +++ b/pip/qsharp/interop/qiskit/backends/backend_base.py @@ -12,6 +12,7 @@ from qiskit.circuit import ( QuantumCircuit, ) +from qiskit.version import get_version_info from qiskit.qasm3.exporter import Exporter from qiskit.providers import BackendV2, Options @@ -319,19 +320,26 @@ def _transpile(self, circuit: QuantumCircuit, **options) -> QuantumCircuit: circuit = self.run_qiskit_passes(circuit, options) - orig = self.target.num_qubits - try: - self.target.num_qubits = circuit.num_qubits - transpile_options = self._build_transpile_options(**options) - backend = transpile_options.pop("backend", self) - target = transpile_options.pop("target", self.target) - # in 1.3 add qubits_initially_zero=True to the transpile call + transpile_options = self._build_transpile_options(**options) + backend = transpile_options.pop("backend", self) + target = transpile_options.pop("target", self.target) + if get_version_info().startswith("1.2"): + # The older Qiskit version does not support the `qubits_initially_zero` option transpiled_circuit = transpile( - circuit, backend=backend, target=target, **transpile_options + circuit, + backend=backend, + target=target, + **transpile_options, ) - return transpiled_circuit - finally: - self.target.num_qubits = orig + else: + transpiled_circuit = transpile( + circuit, + backend=backend, + target=target, + qubits_initially_zero=True, + **transpile_options, + ) + return transpiled_circuit def run_qiskit_passes(self, circuit, options): pass_options = self._build_qiskit_pass_options(**options) diff --git a/pip/qsharp/interop/qiskit/backends/qirtarget.py b/pip/qsharp/interop/qiskit/backends/qirtarget.py index 7954094e95..51db0b0e33 100644 --- a/pip/qsharp/interop/qiskit/backends/qirtarget.py +++ b/pip/qsharp/interop/qiskit/backends/qirtarget.py @@ -61,6 +61,8 @@ def __init__( ): super().__init__(num_qubits=num_qubits) + self._num_qubits = num_qubits + if target_profile != TargetProfile.Base: self.add_instruction(ControlFlowOp, name="control_flow") self.add_instruction(IfElseOp, name="if_else") @@ -121,3 +123,16 @@ def __init__( self.add_instruction(IGate, name="id") self.add_instruction(CHGate, name="ch") + + # NOTE: The follow property intentionally shadows the property on the parent class to allow it to return `None` + # when the value is not set, which allows bypassing transpilation checks for number of qubits. Without this, + # versions of Qiskit 1.3.0 and higher default to `0` which will fail later checks. + @property + def num_qubits(self): + return self._num_qubits + + # NOTE: The follow property setter intentionally shadows the property on the parent class to allow it to be set, which + # maintains compatiblity with Qiskit versions before 1.3.0 where the property was settable. + @num_qubits.setter + def num_qubits(self, value): + self._num_qubits = value diff --git a/pip/tests-integration/test_requirements.txt b/pip/tests-integration/test_requirements.txt index 74b8528928..e60aedadc9 100644 --- a/pip/tests-integration/test_requirements.txt +++ b/pip/tests-integration/test_requirements.txt @@ -1,5 +1,5 @@ pytest==8.2.2 -qiskit>=1.2.2,<1.3.0 +qiskit>=1.2.2,<2.0.0 qirrunner==0.7.1 pyqir==0.10.2 qiskit-aer==0.14.2 From 9a1ef7ba8e706cf39f314bbf1513b06c520449f4 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 2 Dec 2024 23:31:35 -0800 Subject: [PATCH 2/4] Revert extra formatting changes --- build.py | 41 ++++++++++------------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/build.py b/build.py index d654c0d38b..0ee53ec667 100755 --- a/build.py +++ b/build.py @@ -69,10 +69,10 @@ ) parser.add_argument( - "--ci-bench", - action=argparse.BooleanOptionalAction, - default=False, - help="Run the benchmarking script that is run in CI (default is --no-ci-bench)", + "--ci-bench", + action=argparse.BooleanOptionalAction, + default=False, + help="Run the benchmarking script that is run in CI (default is --no-ci-bench)", ) args = parser.parse_args() @@ -303,46 +303,25 @@ def run_python_integration_tests(cwd, interpreter): def run_ci_historic_benchmark(): branch = "main" output = subprocess.check_output( - [ - "git", - "rev-list", - "--since=1 week ago", - "--pretty=format:%ad__%h", - "--date=short", - branch, - ] + ["git", "rev-list", "--since=1 week ago", "--pretty=format:%ad__%h", "--date=short", branch] ).decode("utf-8") - print("\n".join([line for i, line in enumerate(output.split("\n")) if i % 2 == 1])) + print('\n'.join([line for i, line in enumerate(output.split('\n')) if i % 2 == 1])) output = subprocess.check_output( - [ - "git", - "rev-list", - "--since=1 week ago", - "--pretty=format:%ad__%h", - "--date=short", - branch, - ] + ["git", "rev-list", "--since=1 week ago", "--pretty=format:%ad__%h", "--date=short", branch] ).decode("utf-8") - date_and_commits = [line for i, line in enumerate(output.split("\n")) if i % 2 == 1] + date_and_commits = [line for i, line in enumerate(output.split('\n')) if i % 2 == 1] for date_and_commit in date_and_commits: print("benching commit", date_and_commit) result = subprocess.run( - [ - "cargo", - "criterion", - "--message-format=json", - "--history-id", - date_and_commit, - ], + ["cargo", "criterion", "--message-format=json", "--history-id", date_and_commit], capture_output=True, - text=True, + text=True ) with open(f"{date_and_commit}.json", "w") as f: f.write(result.stdout) - if build_pip: step_start("Building the pip package") From e16380a3cbde78379a1d0636d2b08ec9689829dd Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 3 Dec 2024 09:32:26 -0800 Subject: [PATCH 3/4] Skip optimization for consistent testing --- .../interop_qiskit/test_gate_correctness.py | 8 +++++++- .../interop_qiskit/test_gateset_qasm.py | 3 +++ pip/tests-integration/interop_qiskit/test_re.py | 10 +++++----- pip/tests-integration/test_requirements.txt | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/pip/tests-integration/interop_qiskit/test_gate_correctness.py b/pip/tests-integration/interop_qiskit/test_gate_correctness.py index b2cf697cf3..723bb9f650 100644 --- a/pip/tests-integration/interop_qiskit/test_gate_correctness.py +++ b/pip/tests-integration/interop_qiskit/test_gate_correctness.py @@ -242,7 +242,13 @@ def _test_circuit( ): target_profile = TargetProfile.Base seed = 42 - backend = QSharpBackend(target_profile=target_profile, seed=seed) + backend = QSharpBackend( + target_profile=target_profile, + seed=seed, + transpile_options={ + "optimization_level": 0 # Use no optimization to get consistent results in simulations + }, + ) try: job = backend.run(circuit, shots=num_shots) result = job.result() diff --git a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py index 66df35227c..68245e33ce 100644 --- a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py +++ b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py @@ -16,6 +16,9 @@ def run_transpile_test( ) -> None: circuit = QuantumCircuit(3, 3) operation(circuit) + if "optimization_level" not in options: + # Use no optimization so gate transpilation is consistent + options["optimization_level"] = 0 info = QSharpBackend()._qasm3(circuit, **options) lines = info.splitlines() # remove the first four lines, which are the header diff --git a/pip/tests-integration/interop_qiskit/test_re.py b/pip/tests-integration/interop_qiskit/test_re.py index 01777ca841..d4dee46571 100644 --- a/pip/tests-integration/interop_qiskit/test_re.py +++ b/pip/tests-integration/interop_qiskit/test_re.py @@ -38,7 +38,7 @@ def test_qsharp_estimation_with_single_params() -> None: for index in range(10): circuit.t(index) circuit.measure(index, index) - sim = ResourceEstimatorBackend() + sim = ResourceEstimatorBackend(transpile_options={"optimization_level": 0}) res = sim.run(circuit, params=params).result() assert res["status"] == "success" @@ -69,8 +69,8 @@ def test_estimate_qiskit_rgqft_multiplier() -> None: { "numQubits": 16, "tCount": 90, - "rotationCount": 958, - "rotationDepth": 658, + "rotationCount": 1002, + "rotationDepth": 680, "cczCount": 0, "ccixCount": 0, "measurementCount": 0, @@ -94,8 +94,8 @@ def test_estimate_qiskit_rgqft_multiplier_in_threadpool() -> None: { "numQubits": 16, "tCount": 90, - "rotationCount": 958, - "rotationDepth": 658, + "rotationCount": 1002, + "rotationDepth": 680, "cczCount": 0, "ccixCount": 0, "measurementCount": 0, diff --git a/pip/tests-integration/test_requirements.txt b/pip/tests-integration/test_requirements.txt index e60aedadc9..ef5088095f 100644 --- a/pip/tests-integration/test_requirements.txt +++ b/pip/tests-integration/test_requirements.txt @@ -1,5 +1,5 @@ pytest==8.2.2 -qiskit>=1.2.2,<2.0.0 +qiskit>=1.3.0,<2.0.0 qirrunner==0.7.1 pyqir==0.10.2 qiskit-aer==0.14.2 From 5ee05d1195fce482973f5adf84e604ef71a05cc2 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 4 Dec 2024 14:53:19 -0800 Subject: [PATCH 4/4] Update build.py --- build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.py b/build.py index 0ee53ec667..5a748a10f4 100755 --- a/build.py +++ b/build.py @@ -514,7 +514,7 @@ def run_ci_historic_benchmark(): "ipykernel", "nbconvert", "pandas", - "qiskit>=1.2.2,<2.0.0", + "qiskit>=1.3.0,<2.0.0", ] subprocess.run(pip_install_args, check=True, text=True, cwd=root_dir, env=pip_env)