Add a wrapper bloq to help plug bloqs into each other whose registers don't quite match
Sometimes we want to use bloqs whose signatures don't quite match up with the signature of a bigger bloq we want to build (for example the bloq might have a flat list of qubits but we have a big register or vice versa). Normally we can just split / join / partition during the decompostion and go on our way, but this leads to unsightly diagrams like this:
Wouldn't it be better if this looked like this:
(the example is a little contrived but I recently hit a case where I wanted to batch HWP rotations for a bloq with a flat list of qubits and didn't want to split/join)
Another use case is we often would like to build up nice diagrams we see in the literature (like QPE / block encoding etc). Currently we have BlackBoxSelect and BlackBoxPrepare which appropriately partition and match registers according to the Select/Prepare Oracle spec. But we may want to plug in some other bloq, (like a walk operator), or something else which requires further black boxes whose construction is a little opaque. It should be simpler if this wrapping was handled automatically.
Here is a prototype for a "Wrap" bloq takes a bloq as an argument and appropriately does the partitions however the user specifies (through a mapping from "outer_registers" to "inner_registers"). It's a bit rough but I think something like this could be useful. It's inspired by BlackBoxPrepare etc and would replace those bloqs like so:
from qualtran.bloqs.hubbard_model import SelectHubbard, PrepareHubbard
from qualtran.bloqs.block_encoding import BlackBoxPrepare, BlackBoxSelect, BlackBoxBlockEncoding
dim = 4
# select = BlackBoxSelect(SelectHubbard(x_dim=dim, y_dim=dim))
sel_hubb = SelectHubbard(x_dim=dim, y_dim=dim)
sel = Register('selection', QAny(sum(r.bitsize for r in sel_hubb.selection_registers)))
sys = Register('system', QAny(sum(r.bitsize for r in sel_hubb.target_registers)))
select = Wrap(sel_hubb, outer_registers=(sel, sys), inner_registers=(sel_hubb.selection_registers, sel_hubb.target_registers))
prepare = BlackBoxPrepare(PrepareHubbard(x_dim=dim, y_dim=dim, t=1, mu=4))
black_box_block_bloq = BlackBoxBlockEncoding(select=select, prepare=prepare)
draw_musical_score(get_musical_score_data(black_box_block_bloq.decompose_bloq()))
The interface / name etc is a little rough so I think perhaps a factory method could replace some of the register mapping, but I think it's sort of helpful.
cc @mpharrigan / @tanujkhattar
it's also possible that this could be added to bloq builder as a utility which would handle some of the boilerplate
sweeeeet.
Since you brought it up: would you be able to come up with meaningful register (soquet) labels so we wouldn't need the short_name title?
Since you brought it up: would you be able to come up with meaningful register (soquet) labels so we wouldn't need the
short_nametitle?
Sure, this is a separate thing though right? I need to look at the drawing code again.