1515import abc
1616from typing import (
1717 Any ,
18+ cast ,
1819 Collection ,
1920 Dict ,
2021 Iterable ,
2728 Union ,
2829)
2930
31+ import attrs
3032import cirq
3133import numpy as np
3234from numpy .typing import NDArray
3840if TYPE_CHECKING :
3941 import quimb .tensor as qtn
4042
41- from qualtran import CtrlSpec , SoquetT
43+ from qualtran import AddControlledT , BloqBuilder , CtrlSpec , SoquetT
4244 from qualtran .cirq_interop import CirqQuregT
4345 from qualtran .drawing import WireSymbol
4446
@@ -357,8 +359,8 @@ def on_registers(
357359 ) -> cirq .Operation :
358360 return self .on (* merge_qubits (self .signature , ** qubit_regs ))
359361
360- def __pow__ (self , power : int ) -> 'Bloq ' :
361- bloq = self if power > 0 else self .adjoint ()
362+ def __pow__ (self , power : int ) -> 'GateWithRegisters ' :
363+ bloq = self if power > 0 else cast ( GateWithRegisters , self .adjoint () )
362364 if abs (power ) == 1 :
363365 return bloq
364366 if all (reg .side == Side .THRU for reg in self .signature ):
@@ -381,15 +383,6 @@ def _get_ctrl_spec(
381383 already accepts a `CtrlSpec` and simply returns it OR a Cirq-style API which accepts
382384 parameters expected by `cirq.Gate.controlled()` and converts them to a `CtrlSpec` object.
383385
384- Users implementing custom `GateWithRegisters.controlled()` overrides can use this helper
385- to generate a CtrlSpec from the cirq-style API and thus easily support both Cirq & Bloq
386- APIs. For example
387-
388- >>> class CustomGWR(GateWithRegisters):
389- >>> def controlled(self, *args, **kwargs) -> 'Bloq':
390- >>> ctrl_spec = self._get_ctrl_spec(*args, **kwargs)
391- >>> # Use ctrl_spec to construct a controlled version of `self`.
392-
393386 Args:
394387 num_controls: Cirq style API to specify control specification -
395388 Total number of control qubits.
@@ -477,10 +470,6 @@ def controlled(
477470 bloq. Bloqs authors can declare their own, custom controlled versions by overriding
478471 `Bloq.get_ctrl_system` in the bloq.
479472
480- If overriding the `GWR.controlled()` method directly, Bloq authors can use the
481- `self._get_ctrl_spec` helper to construct a `CtrlSpec` object from the input parameters of
482- `GWR.controlled()` and use it to return a custom controlled version of this Bloq.
483-
484473
485474 Args:
486475 num_controls: Cirq style API to specify control specification -
@@ -548,3 +537,62 @@ def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.Circ
548537
549538 wire_symbols [0 ] = self .__class__ .__name__
550539 return cirq .CircuitDiagramInfo (wire_symbols = wire_symbols )
540+
541+
542+ class SpecializedSingleQubitControlledGate (GateWithRegisters ):
543+ """Add a specialized single-qubit controlled version of a Bloq.
544+
545+ `control_val` is an optional single-bit control. When `control_val` is provided,
546+ the `control_registers` property should return a single named qubit register,
547+ and otherwise return an empty tuple.
548+
549+ Example usage:
550+
551+ @attrs.frozen
552+ class MyGate(SpecializedSingleQubitControlledGate):
553+ control_val: Optional[int] = None
554+
555+ @property
556+ def control_registers() -> Tuple[Register, ...]:
557+ return () if self.control_val is None else (Register('control', QBit()),)
558+ """
559+
560+ control_val : Optional [int ]
561+
562+ @property
563+ @abc .abstractmethod
564+ def control_registers (self ) -> Tuple [Register , ...]:
565+ ...
566+
567+ def get_single_qubit_controlled_bloq (
568+ self , control_val : int
569+ ) -> 'SpecializedSingleQubitControlledGate' :
570+ """Override this to provide a custom controlled bloq"""
571+ return attrs .evolve (self , control_val = control_val ) # type: ignore[misc]
572+
573+ def get_ctrl_system (
574+ self , ctrl_spec : Optional ['CtrlSpec' ] = None
575+ ) -> Tuple ['Bloq' , 'AddControlledT' ]:
576+ if ctrl_spec is None :
577+ ctrl_spec = CtrlSpec ()
578+
579+ if self .control_val is None and ctrl_spec .shapes in [((),), ((1 ,),)]:
580+ control_val = int (ctrl_spec .cvs [0 ].item ())
581+ cbloq = self .get_single_qubit_controlled_bloq (control_val )
582+
583+ if not hasattr (cbloq , 'control_registers' ):
584+ raise TypeError ("{cbloq} should have attribute `control_registers`" )
585+
586+ (ctrl_reg ,) = cbloq .control_registers
587+
588+ def adder (
589+ bb : 'BloqBuilder' , ctrl_soqs : Sequence ['SoquetT' ], in_soqs : dict [str , 'SoquetT' ]
590+ ) -> tuple [Iterable ['SoquetT' ], Iterable ['SoquetT' ]]:
591+ soqs = {ctrl_reg .name : ctrl_soqs [0 ]} | in_soqs
592+ soqs = bb .add_d (cbloq , ** soqs )
593+ ctrl_soqs = [soqs .pop (ctrl_reg .name )]
594+ return ctrl_soqs , soqs .values ()
595+
596+ return cbloq , adder
597+
598+ return super ().get_ctrl_system (ctrl_spec )
0 commit comments