Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify cbloq.to_cirq_circuit() with sensible defaults and add cbloq.to_cirq_circuit_and_quregs() #927

Merged
merged 7 commits into from
May 9, 2024
38 changes: 32 additions & 6 deletions qualtran/_infra/composite_bloq.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import cirq

from qualtran.cirq_interop._cirq_to_bloq import CirqQuregInT, CirqQuregT
from qualtran.cirq_interop.t_complexity_protocol import TComplexity
from qualtran.resource_counting import BloqCountT, SympySymbolAllocator
from qualtran.simulation.classical_sim import ClassicalValT

Expand Down Expand Up @@ -139,16 +138,19 @@ def as_cirq_op(
"""Return a cirq.CircuitOperation containing a cirq-exported version of this cbloq."""
import cirq

circuit, out_quregs = self.to_cirq_circuit(qubit_manager=qubit_manager, **cirq_quregs)
circuit, out_quregs = self.to_cirq_circuit_and_quregs(
qubit_manager=qubit_manager, cirq_quregs=cirq_quregs
)
return cirq.CircuitOperation(circuit), out_quregs

def to_cirq_circuit(
self, qubit_manager: Optional['cirq.QubitManager'] = None, **cirq_quregs: 'CirqQuregInT'
def to_cirq_circuit_and_quregs(
self, qubit_manager: Optional['cirq.QubitManager'] = None, **cirq_quregs
) -> Tuple['cirq.FrozenCircuit', Dict[str, 'CirqQuregT']]:
"""Convert this CompositeBloq to a `cirq.Circuit`.
"""Convert this CompositeBloq to a `cirq.Circuit` and output qubit registers.

Args:
qubit_manager: A `cirq.QubitManager` to allocate new qubits.
qubit_manager: A `cirq.QubitManager` to allocate new qubits. If not provided,
uses `cirq.SimpleQubitManager()` by default.
**cirq_quregs: Mapping from left register names to Cirq qubit arrays.

Returns:
Expand All @@ -166,6 +168,30 @@ def to_cirq_circuit(
self.signature, cirq_quregs, self._binst_graph, qubit_manager=qubit_manager
)

def to_cirq_circuit(
self,
*,
qubit_manager: Optional['cirq.QubitManager'] = None,
cirq_quregs: Optional[Mapping[str, 'CirqQuregInT']] = None,
) -> 'cirq.FrozenCircuit':
"""Convert this CompositeBloq to a `cirq.Circuit`.

Args:
qubit_manager: A `cirq.QubitManager` to allocate new qubits. If not provided,
uses `cirq.SimpleQubitManager()` by default.
cirq_quregs: Mapping from left register names to Cirq qubit arrays. If not provided,
uses `get_named_qubits(self.signature.lefts())` by default.

Returns:
circuit: The cirq.FrozenCircuit version of this composite bloq.
"""
from qualtran._infra.gate_with_registers import get_named_qubits

if cirq_quregs is None:
cirq_quregs = get_named_qubits(self.signature.lefts())

return self.to_cirq_circuit_and_quregs(qubit_manager=qubit_manager, **cirq_quregs)[0]

@classmethod
def from_cirq_circuit(cls, circuit: 'cirq.Circuit') -> 'CompositeBloq':
"""Construct a composite bloq from a Cirq circuit.
Expand Down
17 changes: 14 additions & 3 deletions qualtran/_infra/composite_bloq_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
)
from qualtran._infra.composite_bloq import _create_binst_graph, _get_dangling_soquets
from qualtran._infra.data_types import BoundedQUInt, QAny, QBit, QFxp, QUInt
from qualtran._infra.gate_with_registers import get_named_qubits
from qualtran.bloqs.basic_gates import CNOT, IntEffect, ZeroEffect
from qualtran.bloqs.for_testing.atom import TestAtom, TestTwoBitOp
from qualtran.bloqs.for_testing.many_registers import TestMultiTypedRegister, TestQFxp
Expand Down Expand Up @@ -164,7 +163,9 @@ def test_map_soqs():

def test_bb_composite_bloq():
cbloq_auto = TestTwoCNOT().decompose_bloq()
circuit, _ = cbloq_auto.to_cirq_circuit(q1=[cirq.LineQubit(1)], q2=[cirq.LineQubit(2)])
circuit, _ = cbloq_auto.to_cirq_circuit_and_quregs(
q1=[cirq.LineQubit(1)], q2=[cirq.LineQubit(2)]
)
cirq.testing.assert_has_diagram(
circuit,
desired="""\
Expand All @@ -174,6 +175,16 @@ def test_bb_composite_bloq():
""",
)

circuit = cbloq_auto.to_cirq_circuit()
cirq.testing.assert_has_diagram(
circuit,
desired="""\
q1: ───@───X───
│ │
q2: ───X───@───
""",
)


def test_bloq_builder():
signature = Signature.build(x=1, y=1)
Expand Down Expand Up @@ -344,7 +355,7 @@ def test_complicated_target_register():
# note: this includes the two `Dangling` generations.
assert len(list(nx.topological_generations(binst_graph))) == 2 * 3 + 2

circuit, _ = cbloq.to_cirq_circuit(None, **get_named_qubits(bloq.signature.lefts()))
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(
circuit,
"""\
Expand Down
4 changes: 2 additions & 2 deletions qualtran/_infra/controlled_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def test_ctrl_bloq_as_cirq_op():

def _test_cirq_equivalence(bloq: Bloq, gate: cirq.Gate):
left_quregs = get_named_qubits(bloq.signature.lefts())
circuit1, right_quregs = bloq.as_composite_bloq().to_cirq_circuit(None, **left_quregs)
circuit1 = bloq.as_composite_bloq().to_cirq_circuit(cirq_quregs=left_quregs)
circuit2 = cirq.Circuit(
gate.on(*merge_qubits(bloq.signature, **get_named_qubits(bloq.signature)))
)
Expand All @@ -124,7 +124,7 @@ def _test_cirq_equivalence(bloq: Bloq, gate: cirq.Gate):
bloq = Controlled(Swap(5), CtrlSpec(qdtypes=QUInt(4), cvs=0b0101))
quregs = get_named_qubits(bloq.signature)
ctrl, x, y = quregs['ctrl'], quregs['x'], quregs['y']
circuit1, _ = bloq.decompose_bloq().to_cirq_circuit(None, **quregs)
circuit1 = bloq.decompose_bloq().to_cirq_circuit(cirq_quregs=quregs)
circuit2 = cirq.Circuit(
cirq.SWAP(x[i], y[i]).controlled_by(*ctrl, control_values=[0, 1, 0, 1]) for i in range(5)
)
Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/basic_gates/hadamard_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_to_cirq():
q = bb.add(OneState())
q = bb.add(Hadamard(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───X───H───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/basic_gates/s_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_to_cirq():
q = bb.add(PlusState())
q = bb.add(SGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───H───S───")


Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/basic_gates/states_and_effects.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@
"outputs": [],
"source": [
"from cirq.contrib.svg import SVGCircuit\n",
"circuit, qubits = cbloq.to_cirq_circuit()\n",
"circuit = cbloq.to_cirq_circuit()\n",
"SVGCircuit(circuit)"
]
}
Expand Down
4 changes: 3 additions & 1 deletion qualtran/bloqs/basic_gates/swap_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ def test_two_bit_swap_unitary_vs_cirq():
def test_two_bit_swap_as_cirq_op():
q = cirq.LineQubit.range(2)
expected_circuit = cirq.Circuit(cirq.SWAP(*q))
cbloq_to_circuit, quregs = TwoBitSwap().as_composite_bloq().to_cirq_circuit(x=[q[0]], y=[q[1]])
cbloq_to_circuit = (
TwoBitSwap().as_composite_bloq().to_cirq_circuit(cirq_quregs={'x': [q[0]], 'y': [q[1]]})
)
cirq.testing.assert_same_circuits(expected_circuit, cbloq_to_circuit)


Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/basic_gates/t_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_to_cirq():
q = bb.add(TGate(), q=q)
q = bb.add(TGate(is_adjoint=True), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───H───T───T^-1───")


Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/basic_gates/toffoli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_toffoli_cirq():
ctrl, target = bb.add(Toffoli(), ctrl=ctrl, target=target)
cbloq = bb.finalize(q0=ctrl[0], q1=ctrl[1], q2=target)

circuit, qubits = cbloq.to_cirq_circuit()
circuit, qubits = cbloq.to_cirq_circuit_and_quregs()
cirq.testing.assert_has_diagram(
circuit,
"""\
Expand Down
4 changes: 2 additions & 2 deletions qualtran/bloqs/basic_gates/x_basis_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_to_cirq():
q = bb.add(PlusState())
q = bb.add(XGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───H───X───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand All @@ -72,7 +72,7 @@ def test_to_cirq():
q = bb.add(MinusState())
q = bb.add(XGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───[ _c(0): ───X───H─── ]───X───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand Down
4 changes: 2 additions & 2 deletions qualtran/bloqs/basic_gates/y_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_to_cirq():
q = bb.add(PlusState())
q = bb.add(YGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───H───Y───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand All @@ -33,7 +33,7 @@ def test_to_cirq():
q = bb.add(MinusState())
q = bb.add(YGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───[ _c(0): ───X───H─── ]───Y───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand Down
4 changes: 2 additions & 2 deletions qualtran/bloqs/basic_gates/z_basis_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def test_to_cirq():
q = bb.add(ZeroState())
q = bb.add(ZGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───Z───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand All @@ -198,7 +198,7 @@ def test_to_cirq():
q = bb.add(OneState())
q = bb.add(ZGate(), q=q)
cbloq = bb.finalize(q=q)
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, "_c(0): ───X───Z───")
vec1 = cbloq.tensor_contract()
vec2 = cirq.final_state_vector(circuit)
Expand Down
5 changes: 3 additions & 2 deletions qualtran/bloqs/util_bloqs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ def test_partition_as_cirq_op():
assert np.allclose(unitary, bloq_to_dense(CNOT()))

bloq = TestPartition(test_bloq=TestMultiRegister())
circuit, _ = bloq.decompose_bloq().to_cirq_circuit(
cirq.ops.SimpleQubitManager(), test_regs=cirq.NamedQubit.range(12, prefix='system')
circuit = bloq.decompose_bloq().to_cirq_circuit(
qubit_manager=cirq.ops.SimpleQubitManager(),
cirq_quregs={'test_regs': cirq.NamedQubit.range(12, prefix='system')},
)
assert (
circuit.to_text_diagram(transpose=True)
Expand Down
4 changes: 2 additions & 2 deletions qualtran/cirq_interop/_bloq_to_cirq.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ def _cirq_style_decompose_from_decompose_bloq(
"""
cbloq = bloq.decompose_bloq()
in_quregs = {reg.name: quregs[reg.name] for reg in bloq.signature.lefts()}
# Input qubits can get de-allocated by cbloq.to_cirq_circuit, thus mark them as managed.
# Input qubits can get de-allocated by cbloq.to_cirq_circuit_and_quregs, thus mark them as managed.
qm = InteropQubitManager(context.qubit_manager)
qm.manage_qubits(merge_qubits(bloq.signature.lefts(), **in_quregs))
circuit, out_quregs = cbloq.to_cirq_circuit(qubit_manager=qm, **in_quregs)
circuit, out_quregs = cbloq.to_cirq_circuit_and_quregs(qubit_manager=qm, **in_quregs)
qubit_map = {q: q for q in circuit.all_qubits()}
for reg in bloq.signature.rights():
if reg.side == Side.RIGHT:
Expand Down
27 changes: 9 additions & 18 deletions qualtran/cirq_interop/_bloq_to_cirq_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,13 @@ def add_my_tensors(


def test_swap_two_bits_to_cirq():
circuit, out_quregs = (
SwapTwoBitsTest()
.as_composite_bloq()
.to_cirq_circuit(
x=[cirq.NamedQubit('q1')],
y=[cirq.NamedQubit('q2')],
qubit_manager=cirq.ops.SimpleQubitManager(),
)
)
circuit = SwapTwoBitsTest().as_composite_bloq().to_cirq_circuit()
cirq.testing.assert_has_diagram(
circuit,
"""\
q1: ───×───
q2: ───×───""",
x: ───×───
y: ───×───""",
)


Expand Down Expand Up @@ -121,12 +113,11 @@ def test_bloq_as_cirq_gate_uses_tensor_data_for_unitary(n: int):


def test_swap():
swap_circuit, _ = (
swap_circuit = (
SwapTest(n=5)
.as_composite_bloq()
.to_cirq_circuit(
x=cirq.LineQubit.range(5),
y=cirq.LineQubit.range(100, 105),
cirq_quregs={'x': cirq.LineQubit.range(5), 'y': cirq.LineQubit.range(100, 105)},
qubit_manager=cirq.ops.SimpleQubitManager(),
)
)
Expand All @@ -151,7 +142,7 @@ def test_multi_and_allocates():
multi_and = MultiAnd(cvs=(1, 1, 1, 1))
cirq_quregs = get_named_qubits(multi_and.signature.lefts())
assert sorted(cirq_quregs.keys()) == ['ctrl']
multi_and_circuit, out_quregs = multi_and.decompose_bloq().to_cirq_circuit(
multi_and_circuit, out_quregs = multi_and.decompose_bloq().to_cirq_circuit_and_quregs(
**cirq_quregs, qubit_manager=cirq.ops.SimpleQubitManager()
)
assert sorted(out_quregs.keys()) == ['ctrl', 'junk', 'target']
Expand Down Expand Up @@ -195,7 +186,7 @@ def test_bloq_as_cirq_gate_left_register():
q = bb.add(XGate(), q=q)
bb.free(q)
cbloq = bb.finalize()
circuit, _ = cbloq.to_cirq_circuit()
circuit = cbloq.to_cirq_circuit()
cirq.testing.assert_has_diagram(circuit, """_c(0): ───alloc───X───free───""")


Expand Down Expand Up @@ -234,7 +225,7 @@ def test_bloq_as_cirq_gate_for_mod_exp():
cbloq = mod_exp.decompose_bloq()
# When converting a composite Bloq to a Cirq circuit, we only need to specify the input
# registers.
decomposed_circuit, out_regs = cbloq.to_cirq_circuit(exponent=quregs['exponent'])
decomposed_circuit, out_regs = cbloq.to_cirq_circuit_and_quregs(exponent=quregs['exponent'])
# Whereas when directly applying a cirq gate on qubits to get an operations, we need to
# specify both input and output registers.
circuit = cirq.Circuit(gate.on_registers(**out_regs), decomposed_circuit)
Expand Down
4 changes: 2 additions & 2 deletions qualtran/cirq_interop/_cirq_to_bloq_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def test_bloq_decompose():
assert tb.pretty_name() == 'TestCNOT'

cirq_quregs = get_named_qubits(tb.signature.lefts())
circuit, _ = tb.decompose_bloq().to_cirq_circuit(**cirq_quregs, qubit_manager=None)
circuit, _ = tb.decompose_bloq().to_cirq_circuit_and_quregs(**cirq_quregs, qubit_manager=None)
assert circuit == cirq.Circuit(cirq.CNOT(*cirq_quregs['control'], *cirq_quregs['target']))
assert tb.t_complexity() == TComplexity(clifford=1)

Expand Down Expand Up @@ -136,7 +136,7 @@ def test_cbloq_to_cirq_circuit():
# Note: a 1d `shape` bloq register is actually two-dimensional in cirq-world
# because of the implicit `bitsize` dimension (which must be explicit in cirq-world).
# CirqGate has registers of bitsize=1 and shape=(n,); hence the list transpose below.
circuit2, _ = cbloq.to_cirq_circuit(
circuit2, _ = cbloq.to_cirq_circuit_and_quregs(
qubits=np.asarray([[q] for q in qubits]), qubit_manager=cirq.ops.SimpleQubitManager()
)

Expand Down
Loading
Loading