API changes for optional registers in QuantumCircuit.#5486
API changes for optional registers in QuantumCircuit.#5486mergify[bot] merged 7 commits intoQiskit:masterfrom
Conversation
mtreinish
left a comment
There was a problem hiding this comment.
So I think this looks good and I like the direction. Just a couple of questions below and a few inline comments/questions:
-
For a register-free bit it basically is just a type/class and a hash/id for the object? Longer term It does make we wonder if we just want to use int indices for bits in QuantumCircuit/DAGCircuit and registers are lists of ints. (ie basically metadata around the bits in a circuit). But that would be a much larger change and probably too hard to make. (this is more just an idle thought, I do think the direction in this PR makes the most sense)
-
Have you done any performance testing on a registerless circuit vs a register one? I know that we've spent a bunch of time tuning the register <-> bit creation and comparisons as operations with bits end up being a lot of the checks we do in the transpiler. Looking at the new bit classes and how
__repr__is changed I think we should look into this ahead of time.
There was a problem hiding this comment.
Is there a difference between this and hash(self)?
There was a problem hiding this comment.
hash(self) would call the overridden Bit.__hash__, so this is only here to sidestep and use the default object hash for new-style bits (while allowing backwards compatibility for old-style). I'll add a comment to clarify, if you're okay with this approach.
There was a problem hiding this comment.
ah ok, yeah that makes sense. Yeah I think having a comment about this is a good idea. It is a bit confusing.
There was a problem hiding this comment.
Comment added in 528f8593f8e2182f9d71303ba11ba8dac44a22b0 .
There was a problem hiding this comment.
I would probably say registerless bit instead of new-style. I think this context around new-style is temporary.
There was a problem hiding this comment.
Agree the context is temporary and only for the transition. I went with old- and new-style over registerless because the new Bits can still be a part of a register, which might be confusing. There are only a few places that call out the differences, so I'm happy to update if you think it's clearer.
There was a problem hiding this comment.
It would be clearer to me. In my head this kind of bit is always registerless even if it's part of a register it's still a registerless bit right (the bit doesn't have a register in its context) but instead the register contains the bits? It's splitting hairs but that's how I would think about it. But, I'll defer to others because what's clearer to me isn't necessarily clear for everyone else and this isn't a big deal either way.
There was a problem hiding this comment.
Okay, good point. For now, I'll leave these in place and when we're writing documentation/release notes, we can decide how best to phrase it (and update the warnings accordingly).
There was a problem hiding this comment.
The only test I would think to add here is one for the mixed register, registerless bit case that index in gates works as expected. But that might be in one of the follow on commits, I haven't looked at those yet.
There was a problem hiding this comment.
Good point. I don't think there's a test covering mixed registered and registerless Bits in the tests covering addition of gates on a specific qubit index (updated in #5498). I'll add one there.
There was a problem hiding this comment.
Is there a reason not to cache this? It definitely will be faster than the repr based on the register case, but we still end up using this a lot (because we use str(qargs) as the sort key for lexicographical_topological_sort()) and this basically gets called once for each bit in an op's qargs list each time we create a new DAGNode. So I wonder what the overhead is going to be for doing the string manipulation and hex conversion once per op vs once per bit.
There was a problem hiding this comment.
This is another sidestep to have new-style Bits look more like placeholder objects (though looking now, similarly using object.__str__ would've been better). I'll update this.
For lexicographical_topological_sort(), I was hoping to remove the string comparison because we would only need an object comparison with new-style Bits, but looking again, rx.lexicographical_topological_sort expects a key function that returns a string. I missed this in the DAGCircuit PR so this is a good catch, I'll update DAGNode.sort_key there and if string construction still shows up in profiling, we can resume caching.
There was a problem hiding this comment.
Yeah doing the string comparison on the retworkx rust side was done for performance. When we first added that function about a year ago having the callback return a string key was a conscious tradeoff between covering a wider set of use cases and performance for how DAGCircuit used it because at least back then the testing showed rust side string comparison to be faster than a python callback returning a bool, but this might have changed some in the past year).
That's right. Long term, converting to
Agree, I've done some preliminary testing on the development branch of the final state (all converted to new-style Bits) but only to check the overall design for large regressions and not covering the intermediate state where we need to check and toggle between old- and new-style (which I would expect to be slower, but not substantially). |
35ce723 to
734e48d
Compare
734e48d to
d18b0e2
Compare
3b53fee to
d18b0e2
Compare
mtreinish
left a comment
There was a problem hiding this comment.
LGTM, a couple more quick inline comments and questions. But nothing really blocking (lets just fix the typo in the exception message before merging).
| return True | ||
|
|
||
| res = False | ||
| if type(self) is type(other) and \ |
There was a problem hiding this comment.
I probably would have used == here but I don't think it matters.
| all( | ||
| # For new-style bits, check bitwise equality. | ||
| sbit == obit | ||
| for sbit, obit in zip(self, other) | ||
| if None in | ||
| (sbit._register, sbit._index, | ||
| obit._register, obit._index) | ||
| ): |
There was a problem hiding this comment.
I know I asked this in an earlier review, but was there performance impact here. In #5272 we removed the bit wise equality check because it had significant overhead for DAGNode creation. I don't think it will come up because it was per bit iirc and only an artifact of Bit.__eq__ previously comparing registers, but it's just something we should keep in mind.
There was a problem hiding this comment.
Agree, much of the transpiler has been updated to avoid register comparisons in #5515 so it's unlikely to be an issue, but agree its something to keep an eye on.
| The constructors of the :class:`~qiskit.circuit.Bit` class and subclasses, | ||
| :class:`~qiskit.circuit.Qubit`, :class:`~qiskit.circuit.Clbit`, and | ||
| :class:`~qiskit.circuit.AncillaQubit`, have been updated such that their | ||
| two parameters, ``register`` and ``index`` are now optional. |
There was a problem hiding this comment.
Maybe have an example here? But, we can just do it in the release time roundup.
There was a problem hiding this comment.
Good point, I'll make sure to add it prior to release.
Summary
Updates to
Bit,Register,QuantumCircuitandDAGCircuitclasses to move fromRegistersas the core data structure unit toward makingBits fundamental andRegisters optional.Details and comments
This PR covers only the API changes required to create and add
Bits toQuantumCircuits,DAGCircuits andRegisters. Subsequent PRs:Bit.registerand.indexinternally inQuantumCircuitandDAGCircuit(and thus supportingRegisters as optional)Bit.indexandBit.registerproperties'q'/'c'registers fromQuantumCircuitint constructorSee working branch master...kdk:quantumcircuit-optional-registers .