Skip to content

Update parameters to accommodate with RZZ constraints #2126

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

Open
wants to merge 37 commits into
base: main
Choose a base branch
from

Conversation

yaelbh
Copy link
Collaborator

@yaelbh yaelbh commented Feb 4, 2025

Summary

Solves #2125 through a function that translates from pub to pub.
Only stared - work is still in progress.

Details and comments

  • In fact, in order not to have to bother with the different structures parameter values can have in pubs, at least for now we translate a pair of circuit and parameter values to another pair, and require the parameter values to be just a list of tuples. The order inside each tuple matches the order of appearance of parameters in the circuit, just like one of the options users have to specify parameter values when defining pubs.
  • Note the discussion in Convert pubs to equivalent pubs that are rzz-valid #2125 about an option that's not implemented here, to do it using a transpiler pass.

@yaelbh yaelbh marked this pull request as draft February 4, 2025 16:48
@yaelbh
Copy link
Collaborator Author

yaelbh commented Feb 9, 2025

I left undone for now:

  1. Support for dynamic circuits.
  2. Preserving the global phase.

Other than that it's ready to play with.

@yaelbh yaelbh marked this pull request as ready for review July 8, 2025 13:20
@yaelbh yaelbh requested review from ElePT and wshanks and removed request for ElePT July 8, 2025 13:20
Copy link
Collaborator

@wshanks wshanks left a comment

Choose a reason for hiding this comment

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

This looks good to me! My main comments are to handle the case of mutlidimensional parameter arrays and to expose the function publicly. My other comments are just suggestions.

if is_rz or is_rx:
# param_exp * 0 to prevent an error complaining that the original parameters,
# still present in the parameter values, are missing from the circuit
param_rzz = param_exp * 0 + Parameter(f"{param_prefix}rzz")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure where the error comes in. It might be worth checking with the Qiskit team if this is the best way to do this. I just worry that a future change could make 0 * param still drop param from the circuit.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@yaelbh do you have an error trace we can look at? I am also not sure what error this is referring to, but I agree that we should take a look.

rzz_angles[idx] = pi / 2 - abs(mod(angle, pi) - pi / 2)

rzz_count += 1
param_prefix = f"rzz_{rzz_count}_"
Copy link
Collaborator

Choose a reason for hiding this comment

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

It might be worth checking if the original circuit has rzz_1_ parameters. I guess that would only happen if function had already been run on the pub and then all the rzz should already be folded and not need new parameters added.

if all(np.isclose(rz_angle, pi) for rz_angle in rz_angles):
new_data.append(
CircuitInstruction(
RZGate(pi),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if this case and the is_x case come up enough to be relevant vs just always using a parameter for consistency.

new_circ.data = new_data

if program_id == "sampler":
return SamplerPub.coerce((new_circ, val_data), pub.shots)
Copy link
Collaborator

Choose a reason for hiding this comment

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

At some point, you need to ravel the new rzz_ parameters back to match the shape of the data in the original pub to support parameter arrays with more than one dimension. For example, this code fails for me on this line:

import numpy as np
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit
from qiskit_ibm_runtime.transpiler.passes.basis.fold_rzz_angle import convert_to_rzz_valid_pub

p1 = Parameter("p1")
p2 = Parameter("p2")

circ = QuantumCircuit(3)
circ.rzz(p1 + p2, 0, 1)
circ.rzz(0.3, 0, 1)
circ.x(0)
circ.rzz(p1 - p2, 2, 1)
convert_to_rzz_valid_pub("sampler", (circ, {"p1": np.ones((3, 2)), "p2": np.ones((3, 2))}))

@@ -244,3 +248,163 @@ def _quad4(angle: float, qubits: Tuple[Qubit, ...]) -> DAGCircuit:
check=False,
)
return new_dag


def convert_to_rzz_valid_pub(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is not a transpiler pass that will be run automatically, do you want to expose it in a more public way by including it in the documentation and possibly providing a higher level import path than qiskit_ibm_runtime.transpiler.passes.basis.fold_rzz_angle?

Copy link
Collaborator

@ElePT ElePT left a comment

Choose a reason for hiding this comment

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

Thanks @yaelbh, sorry for the late review. I added a couple of small comments on top of Will's.

if is_rz or is_rx:
# param_exp * 0 to prevent an error complaining that the original parameters,
# still present in the parameter values, are missing from the circuit
param_rzz = param_exp * 0 + Parameter(f"{param_prefix}rzz")
Copy link
Collaborator

Choose a reason for hiding this comment

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

@yaelbh do you have an error trace we can look at? I am also not sure what error this is referring to, but I agree that we should take a look.



def convert_to_rzz_valid_pub(
program_id: str, pub: Union[SamplerPubLike, EstimatorPubLike]
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this is intended to be user-facing, I feel like it would be smoother to allow the function to accept a primitive instance instead of a program_id. I don't think that the program_id is an externally understood attribute (and it probably shouldn't be), and you can always fetch it from the primitive.

Suggested change
program_id: str, pub: Union[SamplerPubLike, EstimatorPubLike]
primitive: Union[SamplerV2, EstimatorV2], pub: Union[SamplerPubLike, EstimatorPubLike]

Comment on lines +295 to +296
col_indices = [np.where(pub_params == param_name)[0][0] for param_name in param_names]
# col_indices is the indices of columns in the parameter value array that have to be checked
Copy link
Collaborator

Choose a reason for hiding this comment

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

super minor suggestion:

Suggested change
col_indices = [np.where(pub_params == param_name)[0][0] for param_name in param_names]
# col_indices is the indices of columns in the parameter value array that have to be checked
# col_indices is the indices of columns in the parameter value array that have to be checked
col_indices = [np.where(pub_params == param_name)[0][0] for param_name in param_names]

Comment on lines +309 to +319
if (angle + pi / 2) % (2 * pi) >= pi:
rz_angles[idx] = pi
else:
rz_angles[idx] = 0

if angle % pi >= pi / 2:
rx_angles[idx] = pi
else:
rx_angles[idx] = 0

rzz_angles[idx] = pi / 2 - abs(mod(angle, pi) - pi / 2)
Copy link
Collaborator

Choose a reason for hiding this comment

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

When the target allows to store angle bound information, this could be extended to read the bounds from the target.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let me know when it happens.

@ElePT ElePT added enhancement New feature or request Changelog: New Feature Include in the Added section of the changelog labels Jul 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: New Feature Include in the Added section of the changelog enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants