Issue with And bloqs when round-tripping to Cirq and back.
I'm doing something a little strange, but I think that what I'm trying to do is supposed to work.
Basically, I'm taking an Add bloq and converting it to a Cirq circuit with some decomposition rules that stop Cirq from decomposing the And bloqs (which are also instances of GateWithRegisters). Then I'm going back from the Cirq circuit to a composite bloq.
I would expect this to work without errors, but there is some issue with the soquets corresponding to the target registers not being properly freed. I tried to figure it out on my own but I don't understand the magic that happens under the hood well enough.
I'm including a minimal (not) working example below.
from qualtran import QUInt
from qualtran.bloqs.arithmetic import Add
from qualtran.bloqs.mcmt import And
import numpy as np
import cirq
def test_self_contained_adder_issue():
adder_bloq = Add(a_dtype=QUInt(4), b_dtype=QUInt(4))
a_qubits = np.asarray([cirq.LineQubit(i * 3 + 0) for i in range(4)])
b_qubits = np.asarray([cirq.LineQubit(i * 3 + 1) for i in range(4)])
adder_op, _ = adder_bloq.as_cirq_op(
qubit_manager=cirq.SimpleQubitManager(), a=b_qubits, b=a_qubits
)
circuit = cirq.Circuit(adder_op)
def is_and_or_short(op):
if len(op.qubits) <= 2:
return True
if isinstance(op.gate, And):
return True
return False
circuit = cirq.Circuit(cirq.decompose(circuit, keep=is_and_or_short))
cbloq = cirq_optree_to_cbloq(
circuit.all_operations(),
)
The following leads to an error
CompositeBloq.from_cirq_circuit(adder_bloq.decompose_bloq().to_cirq_circuit())
but if you instead specify the input / output registers correct, then it works. So for example:
cbloq = cirq_optree_to_cbloq(adder_bloq.decompose_bloq().to_cirq_circuit(), signature=adder_bloq.signature, in_quregs=get_named_qubits(adder_bloq.signature.lefts()), out_quregs=get_named_qubits(adder_bloq.signature.rights())
@mpharrigan We should probably update the CompositeBloq.from_cirq_circuit so it also accepts optional parameters for signature, in_quregs and out_quregs and forwards it to the cirq_optree_to_cbloq method so users don't need to call the function and can directly call the static method on CompositeBloq.
why does the conversion fail without the additional arguments? Add has only thru registers, so presumably the signature, in_quregs, and out_quregs arguments aren't carrying any special information.
Would it be possible to raise a more helpful error message in these cases?
I see that if signature is None it uses circuit.all_qubits() to figure out the signature, which must include all the ancillae
which must include all the ancillae
The word "must" could mean different things here. Should this be interpreted as "it needs to (but it doesn't)" or "it seems to (but it shouldn't)"?
it uses all_qubits to deduce a signature that includes registers for qubits that only exist internally as ancillae (local q-variables), and shouldn't be included in the signature
Okay, so is it that cirq's all_qubits includes ancillae by default, and needs a flag to exclude them? Or is it that the conversion from qualtran format to cirq format preemptively materialize all the ancilla qubits into regular qubits, so that cirq would have no way to tell the difference, in which case the fix would need to be on the qualtran side?
There is actual information lost when converting to Cirq that cannot be automatically recovered. The most concise form of the data that needs to be recovered is indeed the signature, in_quregs, and out_quregs arguments that one can provide to Qualtran to finish the round trip