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

Add support for Qubit registers to Cirq #6053

Closed
tanujkhattar opened this issue Apr 4, 2023 · 6 comments · Fixed by #6200
Closed

Add support for Qubit registers to Cirq #6053

tanujkhattar opened this issue Apr 4, 2023 · 6 comments · Fixed by #6200
Labels
area/cirq-ft Issues related to the Cirq-FT sub-package area/gates area/qubits kind/design-issue A conversation around design kind/roadmap-item for higher level roadmap items to capture conversations and feedback (not for project tracking) triage/accepted there is consensus amongst maintainers that this is a real bug or a reasonable feature to add

Comments

@tanujkhattar
Copy link
Collaborator

tanujkhattar commented Apr 4, 2023

RFC

Background

Cirq has abstractions to represent individual qubits (eg: LineQid, GridQid, NamedQid etc.) and gates act on qubits to produce operations (often using op = gate.on(*qubits)). However, this approach doesn’t scale very well as we construct larger composite gates that can act on a larger number of qubits; many of which are logically a single grouped entity. For example, the ArithmeticGate is used to do arithmetic operations on integers represented by “registers” of qubits. Right now, a user needs to manually apply an arithmetic gate on a set of qubits and ensure that the implicit ordering of qubits is correct.

The goal of this design discussion is to make it easier to work with logical groups of qubits (i.e. qubit registers) throughout the Cirq stack.

Design Details

Introduce Register dataclass and Registers container type for representing qubit registers

A Register represents the a logical set of qubits that a gate is expected to act upon. Note that the Register doesn't actually store any qubits -- it just represents an expectation of a (potentially multi-dimensional) sequence of qubits and assigns a name to this logical group. Registers represents a collection of individual Register instances.

The Register and Registers classes together represent a more sophisticated way of specifying _num_qubits_ for a gate in Cirq -- i.e. a cirq gate can expose a Registers object, that conveys the number of input wires to that gate along with a name and size attached to each input wire.

@attrs.frozen
class Register:
    name: str
    bitsize: int
    # Multi dimensional registers can be supported by adding a shape attribute.
    # shape: Tuple[int, ...]

class Registers:
    def __init__(self, registers: Iterable[Register]):
        ...
    @classmethod
    def build(cls, **registers: Union[int, Tuple[int, ...]]) -> 'Registers':
        return cls(Register(name=k, bitsize=v) for k, v in registers.items())
    @property
    def bitsize(self) -> int:
        return sum(reg.bitsize for reg in self)

Make it easier to write a cirq.Gate that operate on qubit registers.

Introduce a new abstract base class GateWithRegisters, derived from cirq.Gate, that exposes a new interfaces to:

  • Define the input/output qubit Registers that the gate acts upon (named)
  • Define an on_registers() interface that can be used to "apply" the gate on named qubit registers, instead of a list of qubits, to get operations.
  • Define a new decompose_from_registers interface, which accepts sequence of qubits as a keyword argument for each named input register.

See the following snippet for a better understanding.

class GateWithRegisters(cirq.Gate, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def registers(self) -> Registers:
        ...

    def _num_qubits_(self) -> int:
        return self.registers.bitsize

    @abc.abstractmethod
    def decompose_from_registers(self, **qubit_regs: Sequence[cirq.Qid]) -> cirq.OP_TREE:
        ...

    def _decompose_(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE:
        qubit_regs = self.registers.split_qubits(qubits)
        yield from self.decompose_from_registers(**qubit_regs)

    def on_registers(self, **qubit_regs: Union[cirq.Qid, Sequence[cirq.Qid]]) -> cirq.Operation:
        return self.on(*self.registers.merge_qubits(**qubit_regs))

This makes it significantly easier for users to define new gates that act upon qubit registers and apply these gates on qubits to get back operations.

Optional Non-Blocking Features

Add multi qubit register versions of existing common gates

Once we have the GateWithRegisters infrastructure setup, we should add multi qubit versions of existing common gates like Multi Control Multi Target CNOT / CZ, Multi Target SWAP etc.

Add container types to manage the Sequence[cirq.Qid]

One can consider adding additional container types to manage the sequences of cirq.Qid that will be passed around gates to construct operations. Specifically, container types to represent LittleEndian vs BigEndian encodings of integers corresponding to qubit registers can be added, which would be useful for users to correctly apply gate with registers on ordered sequences of qubits.

Add support for qubit registers in diagramming utilities

If a circuit is composed entire of multi qubit register operations acting on well defined qubit registers, we can update the Circuit.to_text_diagram_drawer method to draw a single wire corresponding to the entire qubit register instead of 1 per qubit. However, this would work only for limited cases when a single multi-qubit wire is not "split" in between the circuit. For more general drawing capabilities supporting multi-qubit wires; we'd need an overhaul of the TextDiagramDrawer.

Conclusion

I'd love to hear thoughts of other maintainers and contributors, specifically @daxfohl @dstrain115 @dabacon. Please let me know if there are additional features that are relevant upon adding support for qubit registers but are not captured in the original proposal. Once there is consensus, I'll create smaller subtasks which can be taken up by 20%ers and other community contributors.

@tanujkhattar tanujkhattar added kind/design-issue A conversation around design triage/discuss Needs decision / discussion, bring these up during Cirq Cynque kind/roadmap-item for higher level roadmap items to capture conversations and feedback (not for project tracking) area/gates area/circuits area/qubits and removed area/circuits labels Apr 4, 2023
@daxfohl
Copy link
Contributor

daxfohl commented Apr 4, 2023

My first thought is there's nothing here that can't be done just using arrays of qubits in code. Is it worth the added complexity?

Second thought is that diagramming is an interesting use case that isn't directly doable without formalizing registers. But to enable that, would it then be disallowed to reference a single qubit in a register? Otherwise, how would you diagram that? Like if you had an ArithmeticGate on a register, and later had a CX on one qubit of that register, you'd have to have a horizontal line for the register and a horizontal line for the qubit, which would be weird. But disallowing referencing a single qubit would probably break decomposition; you couldn't decomp a gate referencing a register if there was some other gate that referenced the same register. Anyway, a lot to think about there and IDK if the ROI is there just for diagramming.

Third thought is serialization, need some way to do this to make registers preserve their meaning when serialized.

Fourth thought is maybe look to Verilog or VHDL for inspiration.

Fifth thought is, is there any reason a register can't just be a struct, that can contain qubits, classical bits, constants? Probably a dumb idea but figured I'd mention it. ArithmeticGate can operate on more than just qubits.

@tanujkhattar
Copy link
Collaborator Author

My first thought is there's nothing here that can't be done just using arrays of qubits in code. Is it worth the added complexity?

One of the primary objectives of introducing registers is to make it easier for users to "manage" these arrays of qubits. The easier management includes a) having a better interface for gates and not worry about supplying qubits in the right order when calling gate.on(*qubits) or splitting a sequence of qubits in the right sizes within _decompose_(self, qubits) etc. As someone who has tried using this feature for writing composite gates that act on registers of qubits; I can confirm that the added value here is significantly higher than the slightly increased code complexity.

@dstrain115
Copy link
Collaborator

Cirq cync decision: will leave open for discussion for two more weeks and also break it up into sub-tasks

@senecameeks senecameeks added triage/accepted there is consensus amongst maintainers that this is a real bug or a reasonable feature to add and removed triage/discuss Needs decision / discussion, bring these up during Cirq Cynque labels May 17, 2023
@senecameeks
Copy link
Collaborator

Cirq cync decision: need to break into sub-tasks, open to 20%ers

@mpharrigan
Copy link
Collaborator

Is this considered done now with cirq_ft?

@mpharrigan mpharrigan added the area/cirq-ft Issues related to the Cirq-FT sub-package label Jul 7, 2023
@mpharrigan
Copy link
Collaborator

bump

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/cirq-ft Issues related to the Cirq-FT sub-package area/gates area/qubits kind/design-issue A conversation around design kind/roadmap-item for higher level roadmap items to capture conversations and feedback (not for project tracking) triage/accepted there is consensus amongst maintainers that this is a real bug or a reasonable feature to add
Projects
No open projects
Status: Done
Development

Successfully merging a pull request may close this issue.

5 participants