6262if TYPE_CHECKING :
6363 import cirq
6464
65+ import qualtran as qlt
66+ import qualtran .dtype as qdt
6567 from qualtran .bloqs .bookkeeping .auto_partition import Unused
6668 from qualtran .cirq_interop ._cirq_to_bloq import CirqQuregInT , CirqQuregT
6769 from qualtran .drawing import WireSymbol
@@ -467,7 +469,7 @@ def final_soqs(self) -> Dict[str, _SoquetT]:
467469
468470 def copy (self ) -> 'CompositeBloq' :
469471 """Create a copy of this composite bloq by re-building it."""
470- bb , _ = BloqBuilder .from_signature (self .signature )
472+ bb , _ = BloqBuilder .from_signature (self .signature , bloq_key = self . bloq_key )
471473 soq_map = bb .initial_soq_map (self .signature .lefts ())
472474
473475 for binst , in_soqs , old_out_soqs in self .iter_bloqsoqs ():
@@ -641,6 +643,11 @@ def __str__(self):
641643 return self .bloq_key
642644 return f'CompositeBloq([{ len (self .bloq_instances )} subbloqs...])'
643645
646+ def __repr__ (self ):
647+ if self .bloq_key is not None :
648+ return f'CompositeBloq(..., bloq_key={ self .bloq_key !r} )'
649+ return f'CompositeBloq([{ len (self .bloq_instances )} subbloqs...])'
650+
644651
645652def _create_binst_graph (
646653 cxns : Iterable [Connection ], nodes : Iterable [BloqInstance ] = ()
@@ -1098,7 +1105,7 @@ def build_composite_bloq(self, bb: BloqBuilder, q0, q1):
10981105 by the framework or by the `BloqBuilder.from_signature(s)` factory method.
10991106 """
11001107
1101- def __init__ (self , add_registers_allowed : bool = True ):
1108+ def __init__ (self , add_registers_allowed : bool = True , * , bloq_key : Optional [ str ] = None ):
11021109 # To be appended to:
11031110 self ._cxns : List [Connection ] = []
11041111 self ._regs : List [Register ] = []
@@ -1113,6 +1120,8 @@ def __init__(self, add_registers_allowed: bool = True):
11131120 # Whether we can call `add_register` and do non-strict `finalize()`.
11141121 self .add_register_allowed = add_registers_allowed
11151122
1123+ self ._bloq_key = bloq_key
1124+
11161125 def add_register_from_dtype (
11171126 self , reg : Union [str , Register ], dtype : Optional [QCDType ] = None
11181127 ) -> Union [None , QVarT ]:
@@ -1135,7 +1144,7 @@ def add_register_from_dtype(
11351144 from qualtran .symbolics import is_symbolic
11361145
11371146 if not self .add_register_allowed :
1138- raise ValueError (
1147+ raise BloqError (
11391148 "This BloqBuilder was constructed from pre-specified registers. "
11401149 "Ad hoc addition of more registers is not allowed."
11411150 )
@@ -1208,15 +1217,19 @@ def add_register(
12081217
12091218 @classmethod
12101219 def from_signature (
1211- cls , signature : Signature , add_registers_allowed : bool = False
1220+ cls ,
1221+ signature : Signature ,
1222+ add_registers_allowed : bool = False ,
1223+ * ,
1224+ bloq_key : Optional [str ] = None ,
12121225 ) -> Tuple ['BloqBuilder' , Dict [str , QVarT ]]:
12131226 """Construct a BloqBuilder with a pre-specified signature.
12141227
12151228 This is safer if e.g. you're decomposing an existing Bloq and need the signatures
12161229 to match. This constructor is used by `Bloq.decompose_bloq()`.
12171230 """
12181231 # Initial construction: allow register addition for the following loop.
1219- bb = cls (add_registers_allowed = True )
1232+ bb = cls (add_registers_allowed = True , bloq_key = bloq_key )
12201233
12211234 initial_soqs : Dict [str , QVarT ] = {}
12221235 for reg in signature :
@@ -1454,7 +1467,11 @@ def add(self, bloq: Bloq, **in_soqs: SoquetInT):
14541467 be unpacked with tuple unpacking. In this final case, the ordering is according
14551468 to `bloq.signature` and irrespective of the order of `**in_soqs`.
14561469 """
1457- outs = self .add_t (bloq , ** in_soqs )
1470+ try :
1471+ outs = self .add_t (bloq , ** in_soqs )
1472+ except BloqError as be :
1473+ raise BloqError (* be .args ) from be
1474+
14581475 if len (outs ) == 0 :
14591476 return None
14601477 if len (outs ) == 1 :
@@ -1539,17 +1556,53 @@ def add_from(self, bloq: Bloq, **in_soqs: SoquetInT) -> Tuple['QVarT', ...]:
15391556 fsoqs = _map_soqs (cbloq .final_soqs (), soq_map )
15401557 return tuple (fsoqs [reg .name ] for reg in cbloq .signature .rights ())
15411558
1559+ def _change_THRU_to_LEFT (self , reg_name : str ):
1560+ """Used during loose `finalize` to force LEFT registers."""
1561+ for reg_i , reg in enumerate (self ._regs ):
1562+ if reg .name == reg_name :
1563+ break
1564+ else :
1565+ raise AssertionError (f"{ reg_name } doesn't exist in the registers." )
1566+
1567+ if reg .side != Side .THRU :
1568+ raise ValueError (f"{ reg } is supposed to be a THRU register." )
1569+
1570+ new_reg = attrs .evolve (reg , side = Side .LEFT )
1571+
1572+ # Replace in `self._available`
1573+ soqs_to_replace = []
1574+ for soq in self ._available :
1575+ if soq .binst is LeftDangle and soq .reg == reg :
1576+ soqs_to_replace .append (soq )
1577+ for soq in soqs_to_replace :
1578+ self ._available .remove (soq )
1579+ self ._available .add (attrs .evolve (soq , reg = new_reg ))
1580+
1581+ # Replace in `self._cxns`
1582+ for j in range (len (self ._cxns )):
1583+ cxn = self ._cxns [j ]
1584+ if cxn .left .reg == reg :
1585+ new_cxn = attrs .evolve (cxn , left = attrs .evolve (cxn .left , reg = new_reg ))
1586+ self ._cxns [j ] = new_cxn
1587+
1588+ # Replace in `self._regs`
1589+ self ._regs [reg_i ] = new_reg
1590+
15421591 def finalize (self , ** final_soqs : SoquetInT ) -> CompositeBloq :
15431592 """Finish building a CompositeBloq and return the immutable CompositeBloq.
15441593
15451594 This method is similar to calling `add()` but instead of adding a new Bloq,
15461595 it configures the final "dangling" soquets that serve as the outputs for
15471596 the composite bloq as a whole.
15481597
1549- If `self.add_registers_allowed` is set to `True`, additional register
1550- names passed to this function will be added as RIGHT registers. Otherwise,
1551- this method validates the provided `final_soqs` against our list of RIGHT
1552- (and THRU) registers.
1598+ If `self.add_registers_allowed` is set to `False`, the kwqargs to this method must
1599+ exactly match the signature configured for this bloq builder.
1600+ Otherwise:
1601+ - surplus arguments (with register names not in our signature) will be interpreted as
1602+ output quantum variables and we'll add a corresponding RIGHT register.
1603+ - missing arguments (i.e. a register name was introduced somewhere but
1604+ no quantum variable was provided for it), we will set that register to be a
1605+ LEFT register.
15531606
15541607 Args:
15551608 **final_soqs: Keyword arguments mapping the composite bloq's register names to
@@ -1577,6 +1630,12 @@ def _infer_shaped_dtype(soq: SoquetT) -> Tuple['QCDType', Tuple[int, ...]]:
15771630 dtype , shape = _infer_shaped_dtype (np .asarray (soq ))
15781631 self ._regs .append (Register (name = name , dtype = dtype , shape = shape , side = Side .RIGHT ))
15791632
1633+ # If a soquet is missing, we're charitable and consider it de-allocated, see docstring.
1634+ deletable_right_reg_names = [reg .name for reg in self ._regs if reg .side == Side .THRU ]
1635+ for name in deletable_right_reg_names :
1636+ if name not in final_soqs :
1637+ self ._change_THRU_to_LEFT (reg_name = name )
1638+
15801639 return self ._finalize_strict (** final_soqs )
15811640
15821641 def _finalize_strict (self , ** final_soqs : SoquetInT ) -> CompositeBloq :
@@ -1592,16 +1651,25 @@ def _fin(idxed_soq: _QVar, reg: Register, idx: Tuple[int, ...]):
15921651 # close over `RightDangle`
15931652 return self ._add_cxn (RightDangle , idxed_soq , reg , idx )
15941653
1654+ if self ._bloq_key :
1655+ debug_str = f'finalization of { self ._bloq_key } '
1656+ else :
1657+ debug_str = 'finalization'
15951658 _process_soquets (
1596- registers = signature .rights (), debug_str = 'Finalizing' , in_soqs = final_soqs , func = _fin
1659+ registers = signature .rights (), debug_str = debug_str , in_soqs = final_soqs , func = _fin
15971660 )
15981661 if self ._available :
1599- raise BloqError (
1600- f"During finalization, { self ._available } Soquets were not used."
1601- ) from None
1662+ if self ._bloq_key is None :
1663+ ctx = ''
1664+ else :
1665+ ctx = f' of { self ._bloq_key } '
1666+ raise BloqError (f"During finalization{ ctx } , { self ._available } Soquets were not used." )
16021667
16031668 return CompositeBloq (
1604- connections = self ._cxns , signature = signature , bloq_instances = self ._binsts
1669+ connections = self ._cxns ,
1670+ signature = signature ,
1671+ bloq_instances = self ._binsts ,
1672+ bloq_key = self ._bloq_key ,
16051673 )
16061674
16071675 def allocate (
@@ -1653,3 +1721,6 @@ def join(self, soqs: SoquetInT, dtype: Optional[QDType] = None) -> 'Soquet':
16531721 dtype = QAny (n )
16541722
16551723 return self .add (Join (dtype = dtype ), reg = soqs )
1724+
1725+ def in_register (self , name : str , dtype : QCDType , shape = ()) -> Union [None , QVarT ]:
1726+ return self .add_register_from_dtype (Register (name = name , dtype = dtype , shape = shape ))
0 commit comments