-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 flatten option to the NLocal family #10269
Conversation
One or more of the the following people are requested to review this:
|
d68108e
to
1c85276
Compare
A couple of thoughts: Would it make sense to expose flatten as a property too so that you can change how it comes out for visualization without having to construct a whole new identical circuit? In notebooks we show an ansatz (or a combination of feature map and anstaz in ML) - sometimes decomposed at present sometimes not depending on what we want to show. Changing flatten would invalidate it so it needs to get built again. I wonder whether this could/should be done for data_preparation (feature maps) too. QAOAnsatz is not directly an NLocal cct but goes through EvolvedOperatorAnsatz (which UCC in Nature is based on). I saw that was not changed but further see the tests with qaoa failed around passing flatten to the parent. |
Also an FYI from my side: personally, I always decompose my ansatz multiple times even for visualization as I don't find the "non-flattened" structure useful at all. Also: in the past I ran into an issue when storing a subclass of an And speaking of the |
I worry about changing the contents of the circuit dynamically like this if feels kind of error prone (granted I feel that way about blueprint circuits in general though). Maybe it's as easy as setting
Yeah I started small here, but I think we'll want to do this to all the circuit library classes that have wrapped output. The performance difference is so drastically improved. I was tempted to just revert #6634 at first. But figured this approach was a bit more sustainable than a breaking api change revert of a breaking change from 2 yrs ago.
Yeah, I reverted this when I saw. I was just confused because it's in the nlocal directory and was kind of on autopilot when I was making the changes. |
I believe we call |
Max: could you link the circuit in an issue, if you find it, or see if it's a duplicate of #10162? (I suspect it might be the latter.) Matthew's investigation made me realise that Fwiw, I think there are at least a couple of algorithms-y objects that involve two layers of wrapping an object (though off the top of my head, I can't remember what they are) when they were only intended to be one layer. |
I think that also |
Pull Request Test Coverage Report for Build 5590876157
💛 - Coveralls |
This commit adds a new flag `flatten` to the circuit library elements in the NLocal family. This flag is used to avoid the nested wrapping of the output that is done by default in the output of these gates. This is done primarily in the interest of performance. The runtime performance of bind_parameters() is vastly improved with a flattened output compared to the nested object. For example, running: ``` qc = EfficientSU2(100, entanglement="linear", reps=100) qc.measure_all() qc.bind_parameters({x: math.pi / 2 for x in qc.parameters}) ``` the runtime of `bind_parameters()` went from ~390 seconds with the wrapped output to ~0.5 seconds with the flattened output. I think given these results longer term we might want to flip the default behavior to only wrap on user request (likely for visualization as that's the only use case I can think of for the wrapped output). The default value is set to `None` in this PR to facilitate this change. In 0.26/0.45 we can emit a warning if `flatten` is `None` to warn that the default will change in a future release.
f8dafb7
to
7d25500
Compare
This is a good addition 👍🏻 But the current implementation doesn't quite do what I expected from the documentation. For example, the >>> EfficientSU2(3, flatten=True, reps=1).draw()
┌──────────┐┌──────────┐ ┌──────────┐ ┌──────────┐
q_0: ┤ Ry(θ[0]) ├┤ Rz(θ[3]) ├──────────■───────┤ Ry(θ[6]) ├─┤ Rz(θ[9]) ├
├──────────┤├──────────┤ ┌─┴─┐ ├──────────┤┌┴──────────┤
q_1: ┤ Ry(θ[1]) ├┤ Rz(θ[4]) ├──■─────┤ X ├─────┤ Ry(θ[7]) ├┤ Rz(θ[10]) ├
├──────────┤├──────────┤┌─┴─┐┌──┴───┴───┐┌┴──────────┤└───────────┘
q_2: ┤ Ry(θ[2]) ├┤ Rz(θ[5]) ├┤ X ├┤ Ry(θ[8]) ├┤ Rz(θ[11]) ├─────────────
└──────────┘└──────────┘└───┘└──────────┘└───────────┘ However, more complex circuits, such as the >>> EvolvedOperatorAnsatz(op, flatten=True).draw()
┌─────────────────────────────┐
q_0: ┤0 ├
│ │
q_1: ┤1 exp(-it (ZZZ + XXX))(t[0]) ├
│ │
q_2: ┤2 ├
└─────────────────────────────┘ For an argument called "flatten" I would rather expect something that decomposes to standard gates, no matter the nesting: # on this circuit, h rz cx are enough, but this should include all standard gates
>>> transpile(EvolvedOperatorAnsatz(op), basis_gates=["h", "rz", "cx"]).draw()
┌───┐┌──────────────┐┌───┐┌───┐ ┌───┐┌──────────────┐┌───┐┌───┐
q_0: ─────┤ X ├┤ Rz(2.0*t[0]) ├┤ X ├┤ H ├──────────┤ X ├┤ Rz(2.0*t[0]) ├┤ X ├┤ H ├─────
┌───┐└─┬─┘└──────────────┘└─┬─┘├───┤┌───┐┌───┐└─┬─┘└──────────────┘└─┬─┘├───┤┌───┐
q_1: ┤ X ├──■────────────────────■──┤ X ├┤ H ├┤ X ├──■────────────────────■──┤ X ├┤ H ├
└─┬─┘ └─┬─┘├───┤└─┬─┘ └─┬─┘├───┤
q_2: ──■──────────────────────────────■──┤ H ├──■──────────────────────────────■──┤ H ├
└───┘ └───┘ That's analog to what So if this argument is supposed to "flatten", would it be better to decompose to the standard gates? Otherwise we could also rename it to |
I fixed the flatten behavior for The tricky bit for that class was that it was built using other circuit library elements which were proper gates, so from the perspective of the flatten option it was flattened to just gate objects (instead of wrapped circuits containing other circuit library elements). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just tiny comments on consistency on the signature that would be nice to get in, but not required.
@@ -40,6 +41,7 @@ def __init__( | |||
name: str = "EvolvedOps", | |||
parameter_prefix: str | Sequence[str] = "t", | |||
initial_state: QuantumCircuit | None = None, | |||
flatten: Optional[bool] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the rest of the signature (also the Optional
import would need to be removed 🙂)
flatten: Optional[bool] = None, | |
flatten: bool | None = None, |
@@ -90,6 +90,7 @@ def __init__( | |||
skip_unentangled_qubits: bool = False, | |||
initial_state: QuantumCircuit | None = None, | |||
name: str | None = "nlocal", | |||
flatten: Optional[bool] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flatten: Optional[bool] = None, | |
flatten: bool | None = None, |
@@ -39,6 +41,7 @@ def __init__( | |||
initial_state: QuantumCircuit | None = None, | |||
mixer_operator=None, | |||
name: str = "QAOA", | |||
flatten: Optional[bool] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flatten: Optional[bool] = None, | |
flatten: bool | None = None, |
In the interest of time I've enqueued it to merge I'll open an issue based on your comment and track that as a fix post-rc1 (since it's just a doc change) |
* Add flatten option to the NLocal family This commit adds a new flag `flatten` to the circuit library elements in the NLocal family. This flag is used to avoid the nested wrapping of the output that is done by default in the output of these gates. This is done primarily in the interest of performance. The runtime performance of bind_parameters() is vastly improved with a flattened output compared to the nested object. For example, running: ``` qc = EfficientSU2(100, entanglement="linear", reps=100) qc.measure_all() qc.bind_parameters({x: math.pi / 2 for x in qc.parameters}) ``` the runtime of `bind_parameters()` went from ~390 seconds with the wrapped output to ~0.5 seconds with the flattened output. I think given these results longer term we might want to flip the default behavior to only wrap on user request (likely for visualization as that's the only use case I can think of for the wrapped output). The default value is set to `None` in this PR to facilitate this change. In 0.26/0.45 we can emit a warning if `flatten` is `None` to warn that the default will change in a future release. * Add missing nlocal subclasses * Add setter * Fix lint * Fix flatten for EvolvedOperatorAnsatz
Summary
This commit adds a new flag
flatten
to the circuit library elements in the NLocal family. This flag is used to avoid the nested wrapping of the output that is done by default in the output of these gates. This is done primarily in the interest of performance. The runtime performance of bind_parameters() is vastly improved with a flattened output compared to the nested object. For example, running:the runtime of
bind_parameters()
went from ~390 seconds with the default wrapped output to ~0.5 seconds with the flattened output. I think given these results longer term we might want to flip the default behavior to only wrap on user request (likely for visualization as that's the only use case I can think of for the wrapped output). The default value is set toNone
in this PR to facilitate this change. In 0.26/0.45 we can emit a warning ifflatten
isNone
to warn that the default will change in a future release.Details and comments