Skip to content

Add some utilities for T-rex error mitigation#8095

Open
ddddddanni wants to merge 3 commits into
quantumlib:mainfrom
ddddddanni:trex
Open

Add some utilities for T-rex error mitigation#8095
ddddddanni wants to merge 3 commits into
quantumlib:mainfrom
ddddddanni:trex

Conversation

@ddddddanni

@ddddddanni ddddddanni commented May 27, 2026

Copy link
Copy Markdown
Collaborator

This PR introduces the initial building blocks and helper functions required to support T-REX error mitigation in
pauli expectation calculation.

The change includes:
Implement _get_trex_twirled_basis_gate to fetch the appropriate rotation gates (Rx, Ry, X, I) based on the target Pauli basis and flip state.
Implement _generate_random_boolean_choices to generate randomized 2D boolean arrays for twirling operations.
Implement _build_trex_twirled_pauli_circuits to append the appropriate basis twirling gates and joint measurements to a base circuit.

@github-actions github-actions Bot added the size: M 50< lines changed <250 label May 27, 2026

@obriente obriente left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for submitting! The code seems ok, but I'm worried about some forward-compatibility issues, as well as making the workflow as easy as possible to combine with other error mitigation techniques. Would defer to other people on the team whether this is a significant issue or something that we can deal with later.

return sorted(all_qubits)


def _get_twirled_basis_gate(basis: cirq.Pauli, flip: bool) -> cirq.Gate:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The word 'twirl' is used for various things in the literature. Maybe it's better to call this 'trex-twirled' or 'readout-twirled'?

(Would defer to @zlatko-minev for an optimal name choice here.)

return cirq.Rx(rads=-np.pi / 2)


def _build_twirled_pauli_circuits(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The name 'twirl' is used a lot in the literature, I'm not sure if 'twirled_pauli' is a good enough descriptor here? Would defer to @zlatko-minev for a decision here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, I added trex prefix before twirled_pauli

"""Measures expectation values of Pauli strings using T-REX error mitigation.

Args:
circuits_to_pauli: Each object contains

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How is this planned to work with the above code? Is the idea that this will call _build_twirled_pauli_circuits for each circuit in circuits_to_pauli, run this through sampler, and then process the result?

I suggest that we use the workflow that the user takes a circuit, builds the twirled circuits given this input, then runs each twirled circuit through a sampler themselves, and then we provide a function to compile the resulting list of results (or list of expectation values). This would be easier to integrate with other forms of error mitigation.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, I added a "generate_trex_and_readout_circuits" API which will return trex and readout circuits. I will add another method to compile the results.

)
twirl_circuits.append(twirled_circuit)

return twirl_circuits

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@chriseclectic @eliottrosenberg - I think this is the best output one can give right now, but this requires the user keep track of twirl_choices themselves to know what to do with each circuit in twirl_circuits (which can be problematic, especially if we want to combine this with more error mitigation techniques).

Anyway, I guess the main question is - do you think that this will be forwards-compatible with any future changes to circuit data structures?

@ddddddanni ddddddanni marked this pull request as ready for review June 3, 2026 21:36
@ddddddanni ddddddanni requested a review from a team as a code owner June 3, 2026 21:36
@mhucka

mhucka commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

@obriente Would you be willing to act as reviewer for this PR? I mean, you basically have been already :-). Can we assign you to this PR?

@pavoljuhas pavoljuhas added the priority/before-1.7 Finish before the Cirq 1.7 release label Jun 11, 2026

@obriente obriente left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All of my comments were addressed, LGTM

@mhucka mhucka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Spotted some possible minor issues.

results: list[PauliStringMeasurementResult]


class TRexMetadata:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A couple of things:

  • I think this may need a @dataclasses.dataclass decorator. Without the decorator, Python will not create an __init__ method. If the class has no __init__ method, then attempting to instantiate it with arguments will fail at runtime with an error.

  • There does not seem to be a unit test case that instantiates a TRexMetadata. I think it would be worth adding one.

return ops.Rx(rads=np.pi / 2)
elif basis == ops.Y and flip:
return ops.Rx(rads=-np.pi / 2)
else:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A branch for ops.I is missing, but it seems like there ought to be one.


# The Pauli string that is being measured.
pauli_str: ops.PauliString
# A 2D boolean array of shape (num_readout_circuits, num_qubits) indicating

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should num_readout_circuits actually be num_twirls?

Comment on lines +411 to +417
return ops.Ry(rads=-np.pi / 2)
elif basis == ops.X and flip:
return ops.Ry(rads=np.pi / 2)
elif basis == ops.Y and not flip:
return ops.Rx(rads=np.pi / 2)
elif basis == ops.Y and flip:
return ops.Rx(rads=-np.pi / 2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere in this file, in the preexisting code, it uses the lowercase variants of the operators: ops.rx, ops.ry, etc. Would it make sense to use the lowercase variants here too, for consistency?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority/before-1.7 Finish before the Cirq 1.7 release size: M 50< lines changed <250

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants