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

Support symbolic decomposition for state prep via alias sampling #1084

Conversation

tanujkhattar
Copy link
Collaborator

Fixes #951

In general, the cirq-interop decomposition assumes access to individual bits. So anytime we would do bb.split / bb.join in the decomposition, its fine to use the cirq style decomposition as well but if we are not doing any splits or joins (example: for higher level bloqs like state prep alias sampling) its useful to migrate the cirq style decomposition to bloq style decomposition so we can support decomposing bloqs with symbolic register sizes.

More such migrations would follow as we discover bloqs which currently have cirq-style decompose_from_registers implemented but can benefit from a symbolic decomposition. An alternate strategy would be to update the cirq-interop code to support symbolics wherever we can.

@@ -53,6 +53,8 @@ def test_state_prep_alias_sampling_symb():
# Symbolic T-counts
symb_t_counts = int(expected_t_count_expr.subs({L: N, sympy.Symbol(r"\epsilon"): epsilon}))
np.testing.assert_allclose(concrete_t_counts, symb_t_counts, rtol=1e-4)
# Ensure the symbolic bloq can decomposes into a composite bloq
assert_valid_bloq_decomposition(bloq)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also call bloq_autotester for the symbolic example?

@tanujkhattar
Copy link
Collaborator Author

Something is going wrong with the cirq interop again due to which cirq simulation tests for reflection using prepare are failing. This is probably related to the few other places we've seen this happen recently. I'll have to spend more time to figure out exactly whats going wrong.

@anurudhp
Copy link
Contributor

Possible cause: GreedyQubitManager, even with maximize_reuse=True, does not reuse qubits in a bloq style decomposition. For example see the outputs below (for a decomp. with two chained allocate/free pairs):

Script

from typing import Iterator

import cirq
from attrs import frozen
from numpy.typing import NDArray
from qualtran import Bloq, BloqBuilder, GateWithRegisters, Signature, SoquetT
from qualtran.bloqs.basic_gates import CNOT
from qualtran.cirq_interop.testing import GateHelper


@frozen
class MultiAlloc(Bloq):
    @property
    def signature(self) -> "Signature":
        return Signature.build(q=1)

    def build_composite_bloq(
        self, bb: "BloqBuilder", q: "SoquetT"
    ) -> dict[str, "SoquetT"]:
        for _ in range(2):
            a = bb.allocate(1)
            a, q = bb.add(CNOT(), ctrl=a, target=q)
            bb.free(a)

        return {"q": q}


class MultiAllocGWR(GateWithRegisters):
    @property
    def signature(self) -> "Signature":
        return Signature.build(q=1)

    def decompose_from_registers(
        self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid]
    ) -> Iterator[cirq.OP_TREE]:
        (q,) = quregs["q"]
        for _ in range(2):
            (a,) = context.qubit_manager.qalloc(1)
            yield CNOT().on(a, q)
            context.qubit_manager.qfree([a])


def visualize(bloq: Bloq | GateWithRegisters):
    print(bloq)
    gate = GateHelper(
        gate=bloq,
        context=cirq.DecompositionContext(
            cirq.GreedyQubitManager(prefix="_greedy", maximize_reuse=True)
        ),
    )
    print(gate.circuit)
    print()
    print(gate.decomposed_circuit)
    print()


if __name__ == "__main__":
    visualize(MultiAlloc())
    visualize(MultiAllocGWR())

output

MultiAlloc
q: ───q───

_greedy_0: ───alloc────@───free─────────────────────────
                       │
_greedy_1: ───alloc────┼────────────────────@───free────
                       │                    │
q: ───────────Y^-0.5───@───Y^0.5───Y^-0.5───@───Y^0.5───

MultiAllocGWR
q: ───MultiAllocGWR───

_greedy_0: ───@───@───
              │   │
q: ───────────X───X───

@tanujkhattar
Copy link
Collaborator Author

Yup, this is known. See #963 (comment)

@tanujkhattar
Copy link
Collaborator Author

@mpharrigan This is ready for a review

Copy link
Contributor

@anurudhp anurudhp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % minor fixes

@tanujkhattar tanujkhattar mentioned this pull request Jul 27, 2024
@mpharrigan
Copy link
Collaborator

@tanujkhattar do you want to merge this

@tanujkhattar
Copy link
Collaborator Author

tanujkhattar commented Aug 13, 2024

I've addressed the nits and also added an assertion to post init to verify that we only do state prep over 1D register. Opened #1280 to track improvements

Merging now.

@tanujkhattar tanujkhattar enabled auto-merge (squash) August 13, 2024 02:51
@tanujkhattar tanujkhattar merged commit d409550 into quantumlib:main Aug 13, 2024
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unclear how to use symbolics
3 participants