Skip to content

Conversation

@debasmita2102
Copy link

  • Implement a new UnrollBoxes transpiler pass to replace BoxOp “scaffolding” with the gates from the boxed circuit body.

  • Support nested boxes via @trivial_recurse, so boxes inside boxes are unrolled correctly.

  • Add an annotation-aware API (known_annotations: Callable[[dict], bool]) so callers can control which annotations are safe to ignore during unrolling.

  • Fixes Transpiler pass to replace boxes by their contents? #14468

@debasmita2102 debasmita2102 requested a review from a team as a code owner December 1, 2025 10:05
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@coveralls
Copy link

coveralls commented Dec 1, 2025

Pull Request Test Coverage Report for Build 19845452657

Details

  • 11 of 28 (39.29%) changed or added relevant lines in 2 files are covered.
  • 7 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.02%) to 88.373%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/transpiler/passes/unroll_boxes.py 10 27 37.04%
Files with Coverage Reduction New Missed Lines %
crates/circuit/src/parameter/parameter_expression.rs 1 82.3%
crates/circuit/src/parameter/symbol_expr.rs 1 72.94%
crates/qasm2/src/lex.rs 5 91.77%
Totals Coverage Status
Change from base Build 19837335950: -0.02%
Covered Lines: 95745
Relevant Lines: 108342

💛 - Coveralls

@Shobhit21287
Copy link

Hi Debasmita, thanks for doing this! The code is very clean and mostly LGTM. I had a question though.

self.known_annotations = known_annotations or (lambda ann: True)
self.max_depth = max_depth

def _validate_annotations(self, box_op: BoxOp, depth: int = 0) -> bool:

Choose a reason for hiding this comment

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

What's the use of the depth argument exactly? I assume that it's to not unroll BoxOps that exceed a certain depth. Will this work fine for the recursive case with multiple nested Boxes? Also I don't see it being passed in the .run(self,dag) function. Is this supposed to be called by the user as well?

Copy link
Author

Choose a reason for hiding this comment

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

Thank @Shobhit21287 . The depth argument was intended for a future extension where UnrollBoxes would stop unrolling once a configurable nesting depth (max_depth) is reached. Right now _validate_annotations is always called without a depth, so the parameter is effectively unused and recursive calls always see depth == 0


def __init__(
self,
recursive: bool = True,
Copy link
Contributor

@aaryav-3 aaryav-3 Dec 4, 2025

Choose a reason for hiding this comment

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

Hi, thank you for your contribution. I had a couple of clarifications regarding this PR. There is a recursive flag being used over here, and the value is being set. However, I don't see it being reused anywhere else in code. Has it been set for future use? If yes, would you say it makes more sense to keep this addition in a future PR rather than this one?


for ann_dict in getattr(box_op, "annotations", []):
if not self.known_annotations(ann_dict):
return False
Copy link
Contributor

Choose a reason for hiding this comment

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

can we add a test case here to test behavior on passing unknown annotations? To ensure that this callable returns False, but the :class:BoxOp remains in the circuit?

class UnrollBoxes(TransformationPass):
"""Unroll BoxOp operations by replacing them with their internal circuit body."""

def __init__(
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there is merit in writing a short doctstring here, defining the input arguments, their types and short descriptions for the :meth:__init__. Since this is a user-facing functionality.

Copy link
Contributor

@aaryav-3 aaryav-3 left a comment

Choose a reason for hiding this comment

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

Thank you so much for your contribution, this PR is minimally and effectively written to introduce the new transpilar pass, I just had a couple of doubts and clarifications before it is good to go

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.

Transpiler pass to replace boxes by their contents?

5 participants