* Rewrite singleton handling including `SingletonInstruction`
This is a large rewrite of the singleton gate handling, building off the
work done across the library to make the initial implementation work.
There are two main purposes to this commit:
* Make `SingletonInstruction` available in addition to `SingletonGate`,
and have these be easy to maintain in conjunction, not need to
duplicate overrides between them, and not require inheritors to do any
nasty multiple inheritance or the like.
* Fix regressions in the construction time of `SingletonGate` instances.
In the end, this turned into a fairly complete rewrite that completely
switches out the method of defining the classes; it transpires that the
previous way of having the "immutable overrides" on _all_ instances of
the classes was the main source of slowdown, with `__setattr__` being a
large problem. The previous method was far easier from an
implementation perspective, but the runtime costs ended up being too
high.
This new form takes a vastly different strategy, explained in detail in
`qiskit/circuit/singleton.py`. The gist is that we instead make the
singleton instance a _subclass_ of the class object we're creating, and
only it contains the overrides to make itself immutable. We get around
the instantiation problem (`__init__` isn't special and doesn't
skip the forbidden `__setattr__`) by constructing an instance of the
_base_ class, and then dynamically switching in the overrides
afterwards. Since the overrides and the dynamic singleton type of the
instance are designed to be co-operative (including in `__slots__`
layout), this can be done safely.
* Make singleton documentation public
* Add tests of new singleton behaviour
* Fix typos
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
---------
Co-authored-by: Matthew Treinish <mtreinish@kortar.org>