Skip to content

Soquet to _Soquet, _QVar, Soquet and QVar#1831

Open
mpharrigan wants to merge 7 commits intoquantumlib:mainfrom
mpharrigan:2026-03/qvar
Open

Soquet to _Soquet, _QVar, Soquet and QVar#1831
mpharrigan wants to merge 7 commits intoquantumlib:mainfrom
mpharrigan:2026-03/qvar

Conversation

@mpharrigan
Copy link
Collaborator

@mpharrigan mpharrigan commented Mar 16, 2026

In Qualtran, we compose quantum operations into larger operations by connecting the outputs of a predecessor bloq to the input "ports" of a successor bloq. We've used the Soquet frozen attrs class to represent these nodes in the compute graph, which address the "ports" of a particular instance of the bloq.

Previously, the immutable Soquet object represented both the "quantum variables" being passed around during bloq building as well as the nodes of the compute graph. With this PR, these concerns are separated. The compute graph nodes are frozen dataclasses of type _Soquet, and the quantum variables are mutable objects of type _QVar. We will put additional helper attributes and methods onto _QVar to assist in bloq building.

For backwards compatibility, the Soquet name is now a typing.Protocol that encapsulates the duck-typing behavior of _Soquet and _QVar. Bloqs in the wild should not have to update the type annotations in build_composite_bloq
with this backwards compatibilty typing shim.

isinstance(..., Soquet) checks will emit a deprecation warning and return True for either _Soquet or _QVar.

If you're using isinstance(soq, Soquet) to determine whether an item is a single object
or an ndarray of those objects, use BloqBuilder.is_single(x) or BloqBuilder.is_ndarray(x). See the documentation in QVarT for an example.

If you're developing library functionality, you can port isinstance checks to either _Soquet or QVar as appropriate.


Backwards compatibility note: Despite the fundamental change to the data model, this PR is designed to be runtime-backwards compatible with deprecation warnings. The way I've accomplished this is through some dark Python incantations, so no guarantees. Please send reports if this breaks something.

Note that type checkers like mypy will likely still complain about things even if the code runs fine without modification following this PR.


High-priority deprecations

  • Please avoid constructing Soquets yourself. You really shouldn't have been doing this. _Soquet is now "private"
  • If you're copying or modifying CompositeBloq by iterating over connections and using map_soqs, please urgently upgrade your code to use the new idiom where you must initialize the mapping with bb.initial_soq_map. The shim I put in is brittle. BloqBuilder.map_soqs behavior when soq is not in map #1742

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant and well-executed refactoring of the Soquet data model, separating it into _Soquet for graph nodes and _QVar for mutable builder variables. This improves the separation of concerns and maintainability. The backward compatibility shims using protocols and metaclasses are clever and should ease the transition for existing code. The improved error messages are also a great addition. I've found one issue regarding a missing type hint that should be addressed, which aligns with the guideline that type annotations should document intended types.

]
return soq_map

def add_from(self, bloq: Bloq, **in_soqs: SoquetInT):
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The return type annotation for this method is missing. The implementation returns a tuple of soquets, but the signature implies it returns None. This is inconsistent with add_t and how add_from is used in flatten_once.

The previous signature was -> Tuple[SoquetT, ...]. The new signature should likely be -> Tuple['QVarT', ...], or potentially use the same return type logic as add() if a single value or None can be returned.

Suggested change
def add_from(self, bloq: Bloq, **in_soqs: SoquetInT):
def add_from(self, bloq: Bloq, **in_soqs: SoquetInT) -> Tuple['QVarT', ...]:
References
  1. Type annotations should document the intended type, including return types, to clarify the contract of the function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

you're right, robot. I suspect at one point I wanted to make this follow the same return type logic as add() but not in this PR!

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.

1 participant