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

Add AnnotatedOperation.params and fix some control issues #12752

Merged
merged 10 commits into from
Jul 26, 2024

Conversation

Cryoris
Copy link
Contributor

@Cryoris Cryoris commented Jul 10, 2024

Summary

Adds support for AnnotatedOperation.params and, leveraging that, fixes some issues when controlling standard gates that are parameterized. Closes #10311, #10697 and #12135.

Since this PR also changes AnnotatedOperation it's probably too large for a backport?

Details and comments

  • AnnotatedOperation.params is a proxy to to base_op.params, if it exists. If the base_op has no params, returns an empty list (as discussed with the team offline).
  • The annotated argument in Gate.control now is None per default, which allows to set it to True for gates that cannot be eagerly synthesized. A test for each gate that failed previously is added.
  • The S/Sdg gates didn't return CS/CSdg gates for a single control, that's also been fixed.

@Cryoris Cryoris added Changelog: New Feature Include in the "Added" section of the changelog Changelog: Bugfix Include in the "Fixed" section of the changelog labels Jul 10, 2024
@Cryoris Cryoris added this to the 1.2.0 milestone Jul 10, 2024
@Cryoris Cryoris requested a review from a team as a code owner July 10, 2024 09:47
@qiskit-bot
Copy link
Collaborator

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

  • @Cryoris
  • @Qiskit/terra-core
  • @ajavadia

@coveralls
Copy link

coveralls commented Jul 10, 2024

Pull Request Test Coverage Report for Build 10113248178

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 97 of 97 (100.0%) changed or added relevant lines in 18 files are covered.
  • 84 unchanged lines in 5 files lost coverage.
  • Overall coverage increased (+0.01%) to 89.739%

Files with Coverage Reduction New Missed Lines %
qiskit/primitives/containers/bit_array.py 2 95.4%
crates/qasm2/src/lex.rs 3 92.37%
crates/qasm2/src/parse.rs 6 97.61%
qiskit/providers/fake_provider/generic_backend_v2.py 11 95.07%
qiskit/visualization/transition_visualization.py 62 12.5%
Totals Coverage Status
Change from base Build 10099530973: 0.01%
Covered Lines: 66770
Relevant Lines: 74405

💛 - Coveralls

Copy link
Contributor

@alexanderivrii alexanderivrii 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 really great!

One thing I am confused about is the new ternary semantics of annotated: with this PR it can be either False, True or None. Would it have been possible to achieve the same result without introducing the None value but by changing the default in the newly added control methods to annotated=True?

Previously the semantics of annotated was as follows: False - means do not create an annotated gate, this was required for backward compatibility. True - means introduce an annotated gate unless there is a friendlier gate available, e.g. x.control(1, annotated=True) would produce a CX-gate, not an annotated operation.

Comment on lines 12 to 13
they contain unbound parameters. Previously, this raised an error, but
now we create an :class:`.AnnotatedOperation` as placeholder. This
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe say something like: "Previously, this raised an error when such gates were added to the circuit, but ..."

qiskit/circuit/annotated_operation.py Outdated Show resolved Hide resolved
qiskit/circuit/gate.py Outdated Show resolved Hide resolved
qiskit/circuit/library/standard_gates/rxx.py Show resolved Hide resolved
@alexanderivrii
Copy link
Contributor

Could you please also a test for reading/setting params for an annotated operation of an annotated operation of a gate?

@Cryoris
Copy link
Contributor Author

Cryoris commented Jul 10, 2024

Would it have been possible to achieve the same result without introducing the None value but by changing the default in the newly added control methods to annotated=True?

That would've been my preferred solution as well, but I thought the default argument cannot change in between classes derived from the same object. If that's allowed, then it would be the nicer solution 🙂

Edit: but the default value also changes with respect to the number of controls and whether the gate parameters are unbound... so we cannot really set it to True or False in the signature 🤔

@alexanderivrii
Copy link
Contributor

alexanderivrii commented Jul 11, 2024

Suppose we keep the default annotated=False so as not change any previous behavior, but we modify the semantics of gate.control(n, annotated) to the following:

  • if annotated is False, then we do not create an annotated operation, unless it depends on parameters
  • if annotated is True, then we choose the version that is more convenient [as before]

In this way we do not change anything that used to work before, and obtain annotated operations for controlled parametric gates. Personally I prefer this slightly over the ternary semantics, but I am happy to change my opinion.

@Cryoris
Copy link
Contributor Author

Cryoris commented Jul 11, 2024

The advantage of what you're describing is that we don't have to change the signature, which would be nice as it reduces the amount of required change. But this has two downsides:

  1. We're implicitly changing default values (e.g. changing from False to True in some cases)
  2. We're overriding user inputs (e.g. if a user explicitly sets False but we override it to True)

In the None scenario, I think it is easier to understand what default values are chosen, plus users can still force us to use whatever value they want.

if annotated is True, then we choose the version that is more convenient [as before]

I'm not sure I fully understand: if I do RYGate(theta).control(annotated=True) it still uses an AnnotatedOperation even though a CRYGate exists, which we could use.

@alexanderivrii
Copy link
Contributor

Thanks @Cryoris. I am not against merging this PR in the current form, and as it fixes an important set of problems we should definitely include it in 1.2. However, let me try to describe again the two options which I think we have.

First option (as I am proposing). We only have two values annotated=False and annotated=True. I am thinking of these as user-specified hints that the user passes to a gate's control/inverse/power methods, and Qiskit is allowed to override these values. For instance, it can override False to True for controlled parametric gates. And it can override True to False if Qiskit has a native representation of this gate that it can use. Personally, I think that RYGate(theta).control(annotated=True) (with 1 control) should be the CRYGate: since we have it in Qiskit, we should probably use it (in particular, maybe we have a great default definition for this gate). In this case, the default value is always False.

Second option (as you are proposing). We have three values annotated=False, annotated=True, and annotated=None with the default value being None. I am interpreting these as follows. The value None means let Qiskit choose the best possible representation. The value of False should then represent "do not create an AnnotatedOperation and fail horribly when writing RYGate(theta).control(annotated=False)". The value of True means "always introduce an annotated operation, with in particular XGate().control(annotated=True) producing an AnnotatedOperation instead of CXGate()."

After writing this down so explicitly, I actually start liking the second choice much better :). Especially if the default value is None in which case Qiskit decides how the gate should be implemented. And if a user wants to shoot oneself in the foot by providing non-default values, let it be. However, it also gives the user (and transpiler passes) much better control on what the gate really is.

So, provided that we fully agree to go with the second option, the only thing I would like to ask you is to make the semantics of annotated clear both in the documentation and in the release notes. And also please check all the other places where annotated is used to make sure it adheres with this semantics.

- use attribute error
- more clearly state the new None arg in reno and Gate class
@Cryoris
Copy link
Contributor Author

Cryoris commented Jul 22, 2024

Option 2 it is then 🙂 I added more detailed explanations about the None argument and resolved the other comments 👍🏻

Copy link
Contributor

@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.

I think the PR looks very good, just left a few minor wording suggestions.

qiskit/circuit/annotated_operation.py Outdated Show resolved Hide resolved
qiskit/circuit/annotated_operation.py Outdated Show resolved Hide resolved
f"Cannot set attribute ``params`` on the base operation {self.base_op}."
)

def validate_parameter(self, parameter: ParameterValueType) -> ParameterValueType:
Copy link
Contributor

Choose a reason for hiding this comment

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

Small question, would it be more coherent if we called it validate_param? I am a bit on the fence, I tend to prefer full words but I find it strange that the other methods say params and this one says parameter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is coming from Instruction.validate_parameter, to have AnnotatedOperation work as drop-in-replacement 🙂

qiskit/circuit/library/standard_gates/rx.py Outdated Show resolved Hide resolved
qiskit/circuit/library/standard_gates/rx.py Outdated Show resolved Hide resolved
qiskit/circuit/library/standard_gates/rxx.py Outdated Show resolved Hide resolved
releasenotes/notes/annotated-params-116288d5628f7ee8.yaml Outdated Show resolved Hide resolved
releasenotes/notes/annotated-params-116288d5628f7ee8.yaml Outdated Show resolved Hide resolved
ElePT
ElePT previously approved these changes Jul 26, 2024
Copy link
Contributor

@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.

LGTM! I just added two comma-related minor suggestions. After they are applied I think the PR is ready to be merged.

releasenotes/notes/annotated-params-116288d5628f7ee8.yaml Outdated Show resolved Hide resolved
qiskit/circuit/gate.py Outdated Show resolved Hide resolved
Co-authored-by: Elena Peña Tapia <[email protected]>
@ElePT ElePT enabled auto-merge July 26, 2024 14:54
@ElePT ElePT added the stable backport potential The bug might be minimal and/or import enough to be port to stable label Jul 26, 2024
@ElePT ElePT added this pull request to the merge queue Jul 26, 2024
Merged via the queue into Qiskit:main with commit 1512535 Jul 26, 2024
15 checks passed
mergify bot pushed a commit that referenced this pull request Jul 26, 2024
* AnnotatedOp.params support and Gate.control fix

* add reno

* lint

* update reno

* review comments

- use attribute error
- more clearly state the new None arg in reno and Gate class

* review from Elena

* Fix ``AttributeError`` test

* lint

* Apply suggestions from code review

Co-authored-by: Elena Peña Tapia <[email protected]>

---------

Co-authored-by: Elena Peña Tapia <[email protected]>
(cherry picked from commit 1512535)
github-merge-queue bot pushed a commit that referenced this pull request Jul 26, 2024
…12828)

* AnnotatedOp.params support and Gate.control fix

* add reno

* lint

* update reno

* review comments

- use attribute error
- more clearly state the new None arg in reno and Gate class

* review from Elena

* Fix ``AttributeError`` test

* lint

* Apply suggestions from code review

Co-authored-by: Elena Peña Tapia <[email protected]>

---------

Co-authored-by: Elena Peña Tapia <[email protected]>
(cherry picked from commit 1512535)

Co-authored-by: Julien Gacon <[email protected]>
Procatv pushed a commit to Procatv/qiskit-terra-catherines that referenced this pull request Aug 1, 2024
…2752)

* AnnotatedOp.params support and Gate.control fix

* add reno

* lint

* update reno

* review comments

- use attribute error
- more clearly state the new None arg in reno and Gate class

* review from Elena

* Fix ``AttributeError`` test

* lint

* Apply suggestions from code review

Co-authored-by: Elena Peña Tapia <[email protected]>

---------

Co-authored-by: Elena Peña Tapia <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: Bugfix Include in the "Fixed" section of the changelog Changelog: New Feature Include in the "Added" section of the changelog stable backport potential The bug might be minimal and/or import enough to be port to stable
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Gate.control(num_ctrl_qubits) is not working when num_ctrl_qubits > 1
5 participants