Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve implicit qubit permutations warning #421

Merged
merged 24 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

## 0.60.0 (Unreleased)

- Revert a change made in release v0.59.0 where users are warned about implicit qubit permutations in {py:func}`tk_to_qiskit`. This avoids spamming the user with unhelpful warnings when using pytket-qiskit backends. These backends handle implicit permutations automatically.
- Fix an unhelpful warning message about implicit swaps when using optimisation level 2 with {py:class}`AerBackend`
CalMacCQ marked this conversation as resolved.
Show resolved Hide resolved
- Add a boolean `perm_warn` argument to {py:func}`tk_to_qiskit` indicating whether to give a warning if the input {py:class}`Circuit` has an implicit qubit permutation.
CalMacCQ marked this conversation as resolved.
Show resolved Hide resolved
- Add new level 3 optimisation that uses `GreedyPauliSimp`

## 0.59.0 (November 2024)
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Every {py:class}`~pytket.backends.backend.Backend` in pytket has its own {py:met
2 preserve barriers in a circuit, while optimisation level 3 will remove them.

:::{list-table} **Default compilation pass for the IBMQBackend and IBMQEmulatorBackend**
:widths: 25 25 25
:widths: 25 25 25 25
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes an error due to an invalid number of columns

:header-rows: 1

* - optimisation_level = 0
Expand All @@ -208,7 +208,7 @@ Every {py:class}`~pytket.backends.backend.Backend` in pytket has its own {py:met
* - [AutoRebase [2]](inv:#*.AutoRebase)
- [SynthesiseTket](inv:#*.SynthesiseTket)
- [FullPeepholeOptimise](inv:#*.passes.FullPeepholeOptimise)
- [RemoveBarriers](inv:#*.passes.RmoveBarriers)
- [RemoveBarriers](inv:#*pytket._tket.passes.RemoveBarriers)
* - LightSabre [3]
- LightSabre [3]
- LightSabre [3]
Expand Down
3 changes: 2 additions & 1 deletion pytket/extensions/qiskit/backends/aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ def process_circuits(
circuit_batches, batch_order = _batch_circuits(circuits, n_shots_list)

replace_implicit_swaps = self.supports_state or self.supports_unitary
perm_warning = False
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to define a variable, it's only used once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Done dd6e973


for (n_shots, batch), indices in zip(circuit_batches, batch_order):
qcs, ppcirc_strs, tkc_qubits_count = [], [], []
Expand All @@ -296,7 +297,7 @@ def process_circuits(
else:
c0, ppcirc_rep = tkc, None

qc = tk_to_qiskit(c0, replace_implicit_swaps)
qc = tk_to_qiskit(c0, replace_implicit_swaps, perm_warning)

if self.supports_state:
qc.save_state()
Expand Down
27 changes: 25 additions & 2 deletions pytket/extensions/qiskit/qiskit_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

"""Methods to allow conversion between Qiskit and pytket circuit classes
"""
import warnings
from collections import defaultdict
from collections.abc import Iterable
from inspect import signature
Expand Down Expand Up @@ -861,8 +862,16 @@ def append_tk_command_to_qiskit(
supported_gate_rebase = AutoRebase(_protected_tket_gates)


def _has_implicit_permutation(circ: Circuit) -> bool:
"""Returns True if a Circuit has a non-trivial permutation
of qubits, false otherwise."""
return any(q0 != q1 for q0, q1 in circ.implicit_qubit_permutation().items())


def tk_to_qiskit(
tkcirc: Circuit, replace_implicit_swaps: bool = False
tkcirc: Circuit,
replace_implicit_swaps: bool = False,
perm_warning: bool = True,
) -> QuantumCircuit:
"""
Converts a pytket :py:class:`Circuit` to a qiskit :py:class:`qiskit.QuantumCircuit`.
Expand All @@ -871,19 +880,33 @@ def tk_to_qiskit(
If no exact replacement can be found for a part of the circuit then an equivalent
circuit will be returned using the tket gates which are supported in qiskit.

Please note that implicit swaps in a pytket Circuit are not handled by default.
Note that implicit swaps in a pytket Circuit are not handled by default.
Consider using the replace_implicit_swaps flag to replace these implicit swaps with
SWAP gates.

:param tkcirc: A :py:class:`Circuit` to be converted
:param replace_implicit_swaps: Implement implicit permutation by adding SWAPs
to the end of the circuit.
:param perm_warnings: Warn if an input circuit has implicit qubit permutations,
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
CalMacCQ marked this conversation as resolved.
Show resolved Hide resolved
True by default.
:return: The converted circuit
"""
tkc = tkcirc.copy() # Make a local copy of tkcirc
if replace_implicit_swaps:
tkc.replace_implicit_wire_swaps()

if (
_has_implicit_permutation(tkcirc)
and perm_warning
and not replace_implicit_swaps
):
warnings.warn(
"The pytket Circuit contains implicit qubit permutations"
+ " which aren't handled by default."
+ " Consider using the replace_implicit_swaps flag in tk_to_qiskit or"
+ " replacing them using Circuit.replace_implicit_swaps()."
)

qcirc = QuantumCircuit(name=tkc.name)
qreg_sizes: dict[str, int] = {}
for qb in tkc.qubits:
Expand Down
19 changes: 18 additions & 1 deletion tests/qiskit_convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import warnings
from collections import Counter
from math import pi

Expand Down Expand Up @@ -62,7 +63,10 @@
reg_eq,
)
from pytket.extensions.qiskit import IBMQBackend, qiskit_to_tk, tk_to_qiskit
from pytket.extensions.qiskit.backends import qiskit_aer_backend
from pytket.extensions.qiskit.backends import (
AerBackend,
qiskit_aer_backend,
)
from pytket.extensions.qiskit.qiskit_convert import _gate_str_2_optype
from pytket.extensions.qiskit.result_convert import qiskit_result_to_backendresult
from pytket.extensions.qiskit.tket_pass import TketAutoPass, TketPass
Expand Down Expand Up @@ -1166,6 +1170,19 @@ def test_symbolic_param_conv() -> None:
)


def test_implicit_swap_warning() -> None:
c = Circuit(2).H(0).SWAP(0, 1)
c.replace_SWAPs()
c.measure_all()
with pytest.warns(UserWarning, match="The pytket Circuit contains implicit qubit"):
tk_to_qiskit(c)

shots_backend = AerBackend()
with warnings.catch_warnings():
warnings.simplefilter("error")
shots_backend.run_circuit(c)


# https://github.com/CQCL/pytket-qiskit/issues/337
def test_nonregister_bits() -> None:
c = Circuit(1).X(0).measure_all()
Expand Down