In the quantum_graph
submodule, I will extend Bloq
s so that you can have an uneven number of inputs and outputs for a Bloq. This supports allocation, de-allocaiton, and reshaping of Soquets
.
registers
Unlike in Cirq where the bitsize
of a register is eventually fleshed out into a List[Qid]
were each individual bit is an actual object; in my design: the bitsize
dimension is like a view object where we can't pick out individual bits. It's like int32
in classical programming -- you need to do a cast or something to get it to an array of bits.
Also similar to classical programming, you can create multidimensional arrays of int32
. The first n-1 dimensions of this hyperrectable of bits are the dimensions of the array and the final dimension is size 32. I'm doing analogous to this. That means registers will learn two new fields: wireshape and side.
class Side(enum.Flag):
LEFT = enum.auto()
RIGHT = enum.auto()
THRU = LEFT | RIGHT
@frozen
class FancyRegister:
name: str
bitsize: int
wireshape: Tuple[int, ...] = tuple()
side: Side = Side.THRU
soquets
Registers declares the function signature of a bloq. The quantum variables that we plumb through are Soquet
s.
A soquet will now be a particular indexed value-instance: that is -- there will be product(register.wireshape) of them for a given register.
@frozen
class Soquet:
binst: Union[BloqInstance, DanglingT]
reg: FancyRegister
idx: Tuple[int, ...] = field(converter=_to_tuple, default=tuple())
(note that we're changing the second field from reg_name
to the actual register.) Reminder that the user-developer won't construct these objects directly. Rather, they are handled (with lineartyping errorchecking et al) by the BloqBuilder.
# e.g.
array_of_soqs = bb.add(AllocateArrayOfQubits(shape=(2,2), bitsize=10_000))
c1, t1 = bb.add(CNOT(), control=array_of_soqs[0, 0], target=array_of_soqs[0, 1])
c2, t2 = bb.add(CNOT(), control=array_of_soqs[1, 0], target=array_of_soqs[1, 1])
reshaping
Since the last, bitsize
dimension is "sealed" the user-developer can introduce bookkeeping, reshpaing bloqs. For example:
@frozen
class Split(Bloq):
n: int
def registers(self) -> FancyRegisters:
return FancyRegisters(
[
FancyRegister(name='split', bitsize=self.n, wireshape=tuple(), side=Side.LEFT),
FancyRegister(name='split', bitsize=1, wireshape=(self.n,), side=Side.RIGHT),
]
)
Note that the register names are shared because one is LEFT, one is RIGHT and their total number of bits is equal. This can help with error checking and drawing.
bloq builder
BloqBuilder.add()
is now more complicated because it has to differentiate between left (input) and right (output) registers as well as indexing. This complexity is contained within the add method which user-developers will never venture into the guts of.