Skip to content

New implementation of _group_interchangeable_qubits can create duplicate keys in returned Tuple #5148

@vtomole

Description

@vtomole

Description of the issue
#4941 modified the implementation of _group_interchangeable_qubits, but in doing so inadvertently made a breaking change in how the returning Tuple is constructed.

The example code below shows how the constructed tuple would return ((0, frozenset({cirq.LineQubit(0), cirq.LineQubit(1), cirq.LineQubit(3)})), (2, frozenset({cirq.LineQubit(2)}))) with previous implementation.

Now it returns ((0, frozenset({cirq.LineQubit(0), cirq.LineQubit(1)})), (2,frozenset({cirq.LineQubit(2)})), (0, frozenset({cirq.LineQubit(3)}))).

The second tuple has 2 keys that are 0.

How to reproduce the issue

import itertools
from typing import Tuple, FrozenSet, Union, cast, Dict, List

import cirq
from cirq.ops import gate_features


class MyGate(cirq.Gate, cirq.InterchangeableQubitsGate):
    def __init__(self, num_qubits) -> None:
        self._num_qubits = num_qubits

    def num_qubits(self) -> int:
        return self._num_qubits

    def qubit_index_to_equivalence_group_key(self, index: int) -> int:
        if index % 2 == 0:
            return index
        return 0


class OldGroupInterchangeableQubitsGateOperation(cirq.GateOperation):
    def _group_interchangeable_qubits(
        self,
    ) -> Tuple[Union["cirq.Qid", Tuple[int, FrozenSet["cirq.Qid"]]], ...]:

        if not isinstance(self.gate, gate_features.InterchangeableQubitsGate):
            return self.qubits

        groups: Dict[int, List["cirq.Qid"]] = {}
        for i, q in enumerate(self.qubits):
            k = self.gate.qubit_index_to_equivalence_group_key(i)
            if k not in groups:
                groups[k] = []
            groups[k].append(q)
        returned_tuple = tuple(sorted((k, frozenset(v)) for k, v in groups.items()))
        print(returned_tuple)
        return returned_tuple


class NewGroupInterchangeableQubitsGateOperation(cirq.GateOperation):
    def _group_interchangeable_qubits(
        self,
    ) -> Tuple[Union["cirq.Qid", Tuple[int, FrozenSet["cirq.Qid"]]], ...]:
        if not isinstance(self.gate, gate_features.InterchangeableQubitsGate):
            return self.qubits
        else:

            def make_key(i_q: Tuple[int, "cirq.Qid"]) -> int:
                return cast(
                    gate_features.InterchangeableQubitsGate, self.gate
                ).qubit_index_to_equivalence_group_key(i_q[0])

            returned_tuple = tuple(
                (k, frozenset(g for _, g in kg))
                for k, kg in itertools.groupby(enumerate(self.qubits), make_key)
            )
            print(returned_tuple)
            return returned_tuple


qubits = cirq.LineQubit.range(4)
gate = MyGate(4)

print("Old group Tuples")
# prints
# ((0, frozenset({cirq.LineQubit(0), cirq.LineQubit(1), cirq.LineQubit(3)})), (2, frozenset({cirq.LineQubit(2)})))
# ((0, frozenset({cirq.LineQubit(0), cirq.LineQubit(2), cirq.LineQubit(3)})), (2, frozenset({cirq.LineQubit(1)})))
OldGroupInterchangeableQubitsGateOperation(
    gate, [qubits[0], qubits[1], qubits[2], qubits[3]]
) == OldGroupInterchangeableQubitsGateOperation(
    gate, [qubits[3], qubits[2], qubits[1], qubits[0]]
)

print("New group Tuples")
# prints
# ((0, frozenset({cirq.LineQubit(0), cirq.LineQubit(1)})), (2, frozenset({cirq.LineQubit(2)})), (0, frozenset({cirq.LineQubit(3)})))
# ((0, frozenset({cirq.LineQubit(2), cirq.LineQubit(3)})), (2, frozenset({cirq.LineQubit(1)})), (0, frozenset({cirq.LineQubit(0)})))
NewGroupInterchangeableQubitsGateOperation(
    gate, [qubits[0], qubits[1], qubits[2], qubits[3]]
) == NewGroupInterchangeableQubitsGateOperation(
    gate, [qubits[3], qubits[2], qubits[1], qubits[0]]
)

Cirq version
0.14.0

Metadata

Metadata

Assignees

Labels

area/gatesgood for learningFor beginners in QC, this will help picking up some knowledge. Bit harder than "good first issues"kind/bug-reportSomething doesn't seem to work.no QC knowledge neededWant to contribute to Cirq, but don't know quantum computing? This issue is for you.size: S10< lines changed <50triage/acceptedA consensus emerged that this bug report, feature request, or other action should be worked on

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions