Skip to content

Commit

Permalink
WIP: Fixing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
y4izus committed Nov 6, 2024
1 parent 44d478a commit ea18e26
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 46 deletions.
14 changes: 14 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Qiskit IBM AI Local Transpiler",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["tests/ai/test_ai_local_linear_function_synthesis.py::test_ai_local_linear_function_synthesis_wrong_backend"],
"justMyCode": false,
"console": "integratedTerminal"
},
],
}
59 changes: 42 additions & 17 deletions qiskit_ibm_transpiler/ai/synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.providers.backend import BackendV2 as Backend
from qiskit_ibm_runtime import QiskitRuntimeService


from qiskit_ibm_transpiler.wrappers import (
AICliffordAPI,
Expand Down Expand Up @@ -53,15 +56,31 @@ def __init__(
],
coupling_map: Union[List[List[int]], CouplingMap, None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
replace_only_if_better: bool = True,
max_threads: Union[int, None] = None,
**kwargs,
) -> None:
if backend_name:
# TODO: Updates with the final date
logger.warning(
"backend_name will be deprecated in February 2025, please use a backend object instead."
)

if isinstance(coupling_map, CouplingMap):
self.coupling_map = list(coupling_map.get_edges())
else:
self.coupling_map = coupling_map
self.backend_name = backend_name

if backend:
self.backend = backend
elif backend_name:
try:
runtime_service = QiskitRuntimeService()
self.backend = runtime_service.backend(name=backend_name)
except Exception:
raise PermissionError(f"ERROR. Backend not supported ({backend_name})")

self.replace_only_if_better = replace_only_if_better
self.synth_service = synth_service
self.max_threads = max_threads if max_threads else MAX_THREADS
Expand Down Expand Up @@ -98,7 +117,7 @@ def synth_nodes(self, nodes):
synth_inputs,
qargs=qargs,
coupling_map=self.coupling_map,
backend_name=self.backend_name,
backend=self.backend,
)
except TranspilerError as e:
logger.warning(
Expand Down Expand Up @@ -154,16 +173,18 @@ def __init__(
self,
coupling_map: Union[List[List[int]], CouplingMap, None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
replace_only_if_better: bool = True,
max_threads: Union[int, None] = None,
**kwargs,
) -> None:
super().__init__(
AICliffordAPI(**kwargs),
coupling_map,
backend_name,
replace_only_if_better,
max_threads,
synth_service=AICliffordAPI(**kwargs),
coupling_map=coupling_map,
backend_name=backend_name,
backend=backend,
replace_only_if_better=replace_only_if_better,
max_threads=max_threads,
)

def _get_synth_input_and_original(self, node):
Expand Down Expand Up @@ -200,6 +221,7 @@ def __init__(
self,
coupling_map: Union[List[List[int]], CouplingMap, None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
replace_only_if_better: bool = True,
max_threads: Union[int, None] = None,
local_mode: bool = True,
Expand All @@ -212,11 +234,12 @@ def __init__(
)

super().__init__(
ai_synthesis_provider,
coupling_map,
backend_name,
replace_only_if_better,
max_threads,
synth_service=ai_synthesis_provider,
coupling_map=coupling_map,
backend_name=backend_name,
backend=backend,
replace_only_if_better=replace_only_if_better,
max_threads=max_threads,
)

def _get_synth_input_and_original(self, node):
Expand Down Expand Up @@ -250,16 +273,18 @@ def __init__(
self,
coupling_map: Union[List[List[int]], CouplingMap, None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
replace_only_if_better: bool = True,
max_threads: Union[int, None] = None,
**kwargs,
) -> None:
super().__init__(
AIPermutationAPI(**kwargs),
coupling_map,
backend_name,
replace_only_if_better,
max_threads,
synth_service=AIPermutationAPI(**kwargs),
coupling_map=coupling_map,
backend_name=backend_name,
backend=backend,
replace_only_if_better=replace_only_if_better,
max_threads=max_threads,
)

def _get_synth_input_and_original(self, node):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from qiskit import QuantumCircuit
from qiskit.circuit.library import LinearFunction
from qiskit.providers.backend import BackendV2 as Backend

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
Expand All @@ -31,7 +32,7 @@ def transpile(
circuits: List[Union[QuantumCircuit, LinearFunction]],
qargs: List[List[int]],
coupling_map: Union[List[List[int]], None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
) -> List[Union[QuantumCircuit, None]]:
"""Synthetize one or more quantum circuits into an optimized equivalent. It differs from a standard synthesis process in that it takes into account where the linear functions are (qargs)
and respects it on the synthesized circuit.
Expand All @@ -49,9 +50,9 @@ def transpile(
# Although this function is called `transpile`, it does a synthesis. It has this name because the synthesis
# is made as a pass on the Qiskit Pass Manager which is used in the transpilation process.

if not coupling_map and not backend_name:
if not coupling_map and not backend:
raise ValueError(
"ERROR. Either a 'coupling_map' or a 'backend_name' must be provided."
"ERROR. Either a 'coupling_map' or a 'backend' must be provided."
)

n_circs = len(circuits)
Expand All @@ -71,7 +72,7 @@ def transpile(

synthesized_linear_function = AILinearFunctionInference().synthesize(
circuit=circuits[index],
coupling_map=coupling_map,
coupling_map=coupling_map or backend.coupling_map,
circuit_qargs=circuit_qargs,
)

Expand Down
12 changes: 9 additions & 3 deletions qiskit_ibm_transpiler/wrappers/ai_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from qiskit import QuantumCircuit
from qiskit.circuit.library import LinearFunction
from qiskit.quantum_info import Clifford
from qiskit.providers.backend import BackendV2 as Backend

from .base import QiskitTranspilerService

Expand All @@ -34,8 +35,10 @@ def transpile(
circuits: List[Union[QuantumCircuit, Clifford]],
qargs: List[List[int]],
coupling_map: Union[List[List[int]], None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
):
backend_name = getattr(backend, "name", None)

if coupling_map is not None:
transpile_resps = self.request_and_wait(
endpoint="transpile",
Expand Down Expand Up @@ -84,7 +87,7 @@ def transpile(
circuits: List[Union[QuantumCircuit, LinearFunction]],
qargs: List[List[int]],
coupling_map: Union[List[List[int]], None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
) -> List[Union[QuantumCircuit, None]]:
"""Synthetize one or more quantum circuits into an optimized equivalent. It differs from a standard synthesis process in that it takes into account where the linear functions are (qargs)
and respects it on the synthesized circuit.
Expand All @@ -102,6 +105,8 @@ def transpile(
# Although this function is called `transpile`, it does a synthesis. It has this name because the synthesis
# is made as a pass on the Qiskit Pass Manager which is used in the transpilation process.

backend_name = getattr(backend, "name", None)

if not coupling_map and not backend_name:
raise ValueError(
"ERROR. Either a 'coupling_map' or a 'backend_name' must be provided."
Expand Down Expand Up @@ -150,8 +155,9 @@ def transpile(
patterns: List[List[int]],
qargs: List[List[int]],
coupling_map: Union[List[List[int]], None] = None,
backend_name: Union[str, None] = None,
backend: Union[Backend, None] = None,
):
backend_name = getattr(backend, "name", None)

if coupling_map is not None:
transpile_resps = self.request_and_wait(
Expand Down
13 changes: 6 additions & 7 deletions tests/ai/test_ai_local_linear_function_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,16 @@ def test_ai_local_linear_function_synthesis_wrong_backend():
original_circuit.cx(0, 1)
original_circuit.cx(1, 2)

ai_linear_functions_synthesis_pass = PassManager(
[
CollectLinearFunctions(min_block_size=2),
AILinearFunctionSynthesis(backend_name="wrong_backend"),
]
)

with pytest.raises(
PermissionError,
match=r"ERROR. Backend not supported \(\w+\)",
):
ai_linear_functions_synthesis_pass = PassManager(
[
CollectLinearFunctions(min_block_size=2),
AILinearFunctionSynthesis(backend_name="wrong_backend"),
]
)
ai_linear_functions_synthesis_pass.run(original_circuit)


Expand Down
27 changes: 12 additions & 15 deletions tests/ai/test_clifford_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,18 @@
from qiskit_ibm_transpiler.ai.synthesis import AICliffordSynthesis


def test_clifford_wrong_backend(random_circuit_transpiled, caplog):
ai_optimize_cliff = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(backend_name="wrong_backend"),
]
)
ai_optimized_circuit = ai_optimize_cliff.run(random_circuit_transpiled)
assert "couldn't synthesize the circuit" in caplog.text
assert "Keeping the original circuit" in caplog.text
assert (
"User doesn't have access to the specified backend: wrong_backend"
in caplog.text
)
assert isinstance(ai_optimized_circuit, QuantumCircuit)
def test_clifford_wrong_backend(random_circuit_transpiled):
with pytest.raises(
PermissionError,
match=r"ERROR. Backend not supported \(\w+\)",
):
ai_clifford_synthesis_pass = PassManager(
[
CollectCliffords(),
AICliffordSynthesis(backend_name="wrong_backend"),
]
)
ai_clifford_synthesis_pass.run(random_circuit_transpiled)


@pytest.mark.skip(
Expand Down

0 comments on commit ea18e26

Please sign in to comment.