Skip to content

Commit

Permalink
Preparing release qiskit-transpiler-service 0.4.11 (#102)
Browse files Browse the repository at this point in the history
* feat: Improve stability changing retry policy (#98)

* Preparing release qiskit-transpiler-service 0.4.11

* chore: Fix tests after backend retirement (#91)

* Fix tests
  • Loading branch information
jesus-talavera-ibm authored Oct 30, 2024
1 parent 9d7ba90 commit 0001aea
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 72 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### 👾 Security

## [0.4.11] - 2024-10-30

### 🎉 Added

- [#97](https://github.com/Qiskit/qiskit-transpiler-service/pull/97) In case of failure, always retry the GET request to retrieve a result until the timeout is reached. Increase the delay between requests to 5 seconds when we are over 1 minute. Retry transpilation POST request three times in case of failure.

### ⚠️ Deprecated

## [0.4.10] - 2024-09-17
Expand Down Expand Up @@ -171,7 +177,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Publishing first version 0.1.2 for the IBM Quantum Summit.

[Unreleased]: https://github.com/Qiskit/qiskit-transpiler-service/compare/0.4.10...main-qiskit-transpiler-service
[Unreleased]: https://github.com/Qiskit/qiskit-transpiler-service/compare/0.4.11...main-qiskit-transpiler-service
[0.4.11]: https://github.com/Qiskit/qiskit-transpiler-service/compare/0.4.10...0.4.11
[0.4.10]: https://github.com/Qiskit/qiskit-transpiler-service/compare/0.4.9...0.4.10
[0.4.9]: https://github.com/Qiskit/qiskit-transpiler-service/compare/0.4.8...0.4.9
[0.4.8]: https://github.com/Qiskit/qiskit-transpiler-service/compare/0.4.7...0.4.8
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
project = "Qiskit Transpiler Service"
copyright = "2024, IBM Quantum"
author = "IBM Quantum"
release = "0.4.10"
release = "0.4.11"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
43 changes: 30 additions & 13 deletions qiskit_transpiler_service/wrappers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

import itertools
import json
import logging
import os
Expand Down Expand Up @@ -52,6 +53,12 @@ def _get_token_from_system():
return token


def _status_interval_generator(fast_interval, slow_interval, switch_time):
yield from itertools.chain(
itertools.repeat(fast_interval, switch_time), itertools.repeat(slow_interval)
)


class QiskitTranspilerService:
"""A helper class that covers some common basic funcionality for the Qiskit transpiler service"""

Expand Down Expand Up @@ -104,15 +111,13 @@ def get_supported_backends(self):
return resp

def request_status(self, endpoint, task_id):
def _giveup(e):
# Only retry 520 errors
return e.response.status_code != 520

@backoff.on_predicate(
backoff.constant,
lambda res: res.get("state") not in ["SUCCESS", "FAILURE"],
jitter=None,
interval=1, # TODO: Define by config or circuit?
interval=_status_interval_generator(
fast_interval=1, slow_interval=5, switch_time=60
), # TODO: Define by config or circuit?
max_time=self.timeout,
)
@backoff.on_exception(
Expand All @@ -123,7 +128,6 @@ def _giveup(e):
requests.exceptions.JSONDecodeError,
requests.exceptions.HTTPError,
),
giveup=_giveup,
max_time=self.timeout,
)
def _request_status(self, endpoint, task_id):
Expand All @@ -148,14 +152,27 @@ def request_and_wait(self, endpoint: str, body: Dict, params: Dict):
_raise_transpiler_error_and_log(f"Error: {exc}")

def _request_and_wait(self, endpoint: str, body: Dict, params: Dict):
resp = requests.post(
f"{self.url}/{endpoint}",
headers=self.headers,
json=body,
params=params,
@backoff.on_exception(
backoff.expo,
(
requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
requests.exceptions.JSONDecodeError,
requests.exceptions.HTTPError,
),
max_tries=3,
)
resp.raise_for_status()
resp = resp.json()
def _request_transp(endpoint: str, body: Dict, params: Dict):
resp = requests.post(
f"{self.url}/{endpoint}",
headers=self.headers,
json=body,
params=params,
)
resp.raise_for_status()
return resp.json()

resp = _request_transp(endpoint, body, params)
task_id = resp.get("task_id")

result = BackendTaskError(
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

setup(
name="qiskit-transpiler-service",
version="0.4.10",
version="0.4.11",
description="Deprecated: use qiskit-ibm-transpiler (https://pypi.org/project/qiskit-ibm-transpiler/) instead. A library to use Qiskit Transpiler service (https://docs.quantum.ibm.com/transpile/qiskit-transpiler-service) and the AI transpiler passes (https://docs.quantum.ibm.com/transpile/ai-transpiler-passes)",
long_description=README,
long_description_content_type="text/markdown",
Expand Down
4 changes: 2 additions & 2 deletions tests/ai/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ def create_linear_circuit(n_qubits, gates):


@pytest.fixture(scope="module")
def random_circuit_transpiled(backend, cmap_backend):
def random_circuit_transpiled(backend_27q, cmap_backend):
circuit = create_random_circuit(27, 4, 2)
qiskit_lvl3_transpiler = generate_preset_pass_manager(
optimization_level=3, coupling_map=cmap_backend[backend]
optimization_level=3, coupling_map=cmap_backend[backend_27q]
)
return qiskit_lvl3_transpiler.run(circuit)

Expand Down
20 changes: 10 additions & 10 deletions tests/ai/test_clifford_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ def test_clifford_wrong_backend(random_circuit_transpiled, caplog):
@pytest.mark.skip(
reason="Unreliable. It passes most of the times with the timeout of 1 second for the current circuits used"
)
def test_clifford_exceed_timeout(random_circuit_transpiled, backend, caplog):
def test_clifford_exceed_timeout(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_cliff = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(backend_name=backend, timeout=1),
AICliffordSynthesis(backend_name=backend_27q, timeout=1),
]
)
ai_optimized_circuit = ai_optimize_cliff.run(random_circuit_transpiled)
Expand All @@ -52,11 +52,11 @@ def test_clifford_exceed_timeout(random_circuit_transpiled, backend, caplog):
assert isinstance(ai_optimized_circuit, QuantumCircuit)


def test_clifford_wrong_token(random_circuit_transpiled, backend, caplog):
def test_clifford_wrong_token(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_cliff = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(backend_name=backend, token="invented_token_2"),
AICliffordSynthesis(backend_name=backend_27q, token="invented_token_2"),
]
)
ai_optimized_circuit = ai_optimize_cliff.run(random_circuit_transpiled)
Expand All @@ -67,11 +67,11 @@ def test_clifford_wrong_token(random_circuit_transpiled, backend, caplog):


@pytest.mark.disable_monkeypatch
def test_clifford_wrong_url(random_circuit_transpiled, backend, caplog):
def test_clifford_wrong_url(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_cliff = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(backend_name=backend, base_url="https://ibm.com/"),
AICliffordSynthesis(backend_name=backend_27q, base_url="https://ibm.com/"),
]
)
ai_optimized_circuit = ai_optimize_cliff.run(random_circuit_transpiled)
Expand All @@ -80,12 +80,12 @@ def test_clifford_wrong_url(random_circuit_transpiled, backend, caplog):


@pytest.mark.disable_monkeypatch
def test_clifford_unexisting_url(random_circuit_transpiled, backend, caplog):
def test_clifford_unexisting_url(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_cliff = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(
backend_name=backend,
backend_name=backend_27q,
base_url="https://invented-domain-qiskit-transpiler-service-123.com/",
),
]
Expand All @@ -100,11 +100,11 @@ def test_clifford_unexisting_url(random_circuit_transpiled, backend, caplog):
assert isinstance(ai_optimized_circuit, QuantumCircuit)


def test_clifford_function(random_circuit_transpiled, backend):
def test_clifford_function(random_circuit_transpiled, backend_27q):
ai_optimize_cliff = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(backend_name=backend),
AICliffordSynthesis(backend_name=backend_27q),
]
)
ai_optimized_circuit = ai_optimize_cliff.run(random_circuit_transpiled)
Expand Down
12 changes: 6 additions & 6 deletions tests/ai/test_linear_function_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ def test_linear_function_unexisting_url(random_circuit_transpiled, backend, capl
assert isinstance(ai_optimized_circuit, QuantumCircuit)


def test_linear_always_replace(backend, caplog):
def test_linear_always_replace(backend_27q, caplog):
orig_qc = QuantumCircuit(3)
orig_qc.cx(0, 1)
orig_qc.cx(1, 2)
ai_optimize_lf = PassManager(
[
CollectLinearFunctions(),
AILinearFunctionSynthesis(
backend_name=backend, replace_only_if_better=False
backend_name=backend_27q, replace_only_if_better=False
),
]
)
Expand All @@ -131,14 +131,14 @@ def test_linear_always_replace(backend, caplog):
assert isinstance(ai_optimized_circuit, QuantumCircuit)


def test_linear_function_only_replace_if_better(backend, caplog):
def test_linear_function_only_replace_if_better(backend_27q, caplog):
orig_qc = QuantumCircuit(3)
orig_qc.cx(0, 1)
orig_qc.cx(1, 2)
ai_optimize_lf = PassManager(
[
CollectLinearFunctions(min_block_size=2),
AILinearFunctionSynthesis(backend_name=backend),
AILinearFunctionSynthesis(backend_name=backend_27q),
]
)
ai_optimized_circuit = ai_optimize_lf.run(orig_qc)
Expand All @@ -147,11 +147,11 @@ def test_linear_function_only_replace_if_better(backend, caplog):
assert isinstance(ai_optimized_circuit, QuantumCircuit)


def test_linear_function_pass(random_circuit_transpiled, backend, caplog):
def test_linear_function_pass(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_lf = PassManager(
[
CollectLinearFunctions(),
AILinearFunctionSynthesis(backend_name=backend),
AILinearFunctionSynthesis(backend_name=backend_27q),
]
)
ai_optimized_circuit = ai_optimize_lf.run(random_circuit_transpiled)
Expand Down
32 changes: 17 additions & 15 deletions tests/ai/test_permutation_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@


@pytest.fixture
def permutations_circuit(backend, cmap_backend):
coupling_map = cmap_backend[backend]
def permutations_circuit(backend_27q, cmap_backend):
coupling_map = cmap_backend[backend_27q]
cmap = list(coupling_map.get_edges())
orig_qc = QuantumCircuit(27)
for i, j in cmap:
Expand Down Expand Up @@ -64,11 +64,11 @@ def test_permutation_wrong_backend(caplog):
@pytest.mark.skip(
reason="Unreliable. It passes most of the times with the timeout of 1 second for the current circuits used"
)
def test_permutation_exceed_timeout(random_circuit_transpiled, backend, caplog):
def test_permutation_exceed_timeout(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_perm = PassManager(
[
CollectPermutations(min_block_size=2, max_block_size=27),
AIPermutationSynthesis(backend_name=backend, timeout=1),
AIPermutationSynthesis(backend_name=backend_27q, timeout=1),
]
)
ai_optimized_circuit = ai_optimize_perm.run(random_circuit_transpiled)
Expand All @@ -80,11 +80,11 @@ def test_permutation_exceed_timeout(random_circuit_transpiled, backend, caplog):
@pytest.mark.skip(
reason="Unreliable many times. We'll research why it fails sporadically"
)
def test_permutation_wrong_token(random_circuit_transpiled, backend, caplog):
def test_permutation_wrong_token(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_perm = PassManager(
[
CollectPermutations(min_block_size=2, max_block_size=27),
AIPermutationSynthesis(backend_name=backend, token="invented_token_2"),
AIPermutationSynthesis(backend_name=backend_27q, token="invented_token_2"),
]
)
ai_optimized_circuit = ai_optimize_perm.run(random_circuit_transpiled)
Expand All @@ -96,11 +96,13 @@ def test_permutation_wrong_token(random_circuit_transpiled, backend, caplog):
reason="Unreliable many times. We'll research why it fails sporadically"
)
@pytest.mark.disable_monkeypatch
def test_permutation_wrong_url(random_circuit_transpiled, backend):
def test_permutation_wrong_url(random_circuit_transpiled, backend_27q):
ai_optimize_perm = PassManager(
[
CollectPermutations(min_block_size=2, max_block_size=27),
AIPermutationSynthesis(backend_name=backend, base_url="https://ibm.com/"),
AIPermutationSynthesis(
backend_name=backend_27q, base_url="https://ibm.com/"
),
]
)
try:
Expand All @@ -115,12 +117,12 @@ def test_permutation_wrong_url(random_circuit_transpiled, backend):
reason="Unreliable many times. We'll research why it fails sporadically"
)
@pytest.mark.disable_monkeypatch
def test_permutation_unexisting_url(random_circuit_transpiled, backend, caplog):
def test_permutation_unexisting_url(random_circuit_transpiled, backend_27q, caplog):
ai_optimize_perm = PassManager(
[
CollectPermutations(min_block_size=2, max_block_size=27),
AIPermutationSynthesis(
backend_name=backend,
backend_name=backend_27q,
base_url="https://invented-domain-qiskit-transpiler-service-123.com/",
),
]
Expand All @@ -135,9 +137,9 @@ def test_permutation_unexisting_url(random_circuit_transpiled, backend, caplog):
assert isinstance(ai_optimized_circuit, QuantumCircuit)


def test_permutation_collector(permutations_circuit, backend, cmap_backend):
def test_permutation_collector(permutations_circuit, backend_27q, cmap_backend):
qiskit_lvl3_transpiler = generate_preset_pass_manager(
optimization_level=1, coupling_map=cmap_backend[backend]
optimization_level=1, coupling_map=cmap_backend[backend_27q]
)
permutations_circuit = qiskit_lvl3_transpiler.run(permutations_circuit)

Expand All @@ -158,14 +160,14 @@ def test_permutation_collector(permutations_circuit, backend, cmap_backend):
assert not dag.named_nodes("clifford", "Clifford")


def test_permutation_pass(permutations_circuit, backend, caplog):
def test_permutation_pass(permutations_circuit, backend_27q, caplog):

ai_optimize_perm = PassManager(
[
CollectPermutations(max_block_size=27),
AIPermutationSynthesis(backend_name=backend),
AIPermutationSynthesis(backend_name=backend_27q),
]
)
ai_optimized_circuit = ai_optimize_perm.run(permutations_circuit)
assert "Using the synthesized circuit" in caplog.text
assert "Requesting synthesis to the service" in caplog.text
assert isinstance(ai_optimized_circuit, QuantumCircuit)
Loading

0 comments on commit 0001aea

Please sign in to comment.