Skip to content

Commit 5ab231d

Browse files
authored
Slightly optimize ParameterVector construction (Qiskit#10403)
This commit optimizes the construction code for the ParameterVector and ParameterVectorElement classes. The construction of these objects was primarily dominated by the UUID construction time. This is because each element in the vector was calling uuid4() to generate a random unique identifier for each Parameter element. However, we don't need a truly random identifier as we just need to avoid collisions with identically named elements. So instead of calling uuid4() n times this commit opts to call uuid4() once and then just use an index offset of that value. This is slightly faster because we don't need to need run the full uuid4 algorithm each time and instead just wrap an integer in uuid object. While this does slightly increase the change of a collision between identically named ParameterVectors, in practice it should still be sufficiently rare. At the same time the classes are slotted to speed up attribute access and shrink the memory footprint of using these objects. The geometric mean of the runtime of creating 10x 2424000 element ParamterVector objects goes from 15.702722168587812 to 13.738723886030732 sec with this commit. While not a huge improvement it is a consistent incremental gain that doesn't require any data model changes. To make the classes significantly faster we'll likely need to change the data model for Paramter, ParameterVector, etc to facilitate faster creation.
1 parent 35eaf83 commit 5ab231d

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

qiskit/circuit/parameter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class Parameter(ParameterExpression):
5050
bc.draw('mpl')
5151
"""
5252

53+
__slots__ = ("_name", "_uuid", "_hash")
54+
5355
def __new__(cls, name, uuid=None): # pylint: disable=unused-argument
5456
# Parameter relies on self._uuid being set prior to other attributes
5557
# (e.g. symbol_map) which may depend on self._uuid for Parameter's hash

qiskit/circuit/parametervector.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@
1212

1313
"""Parameter Vector Class to simplify management of parameter lists."""
1414

15-
from uuid import uuid4
15+
from uuid import uuid4, UUID
1616

1717
from .parameter import Parameter
1818

1919

2020
class ParameterVectorElement(Parameter):
2121
"""An element of a ParameterVector."""
2222

23+
___slots__ = ("_vector", "_index")
24+
2325
def __new__(cls, vector, index, uuid=None): # pylint:disable=unused-argument
2426
obj = object.__new__(cls)
2527

@@ -34,7 +36,7 @@ def __new__(cls, vector, index, uuid=None): # pylint:disable=unused-argument
3436
def __getnewargs__(self):
3537
return (self.vector, self.index, self._uuid)
3638

37-
def __init__(self, vector, index):
39+
def __init__(self, vector, index, uuid=None): # pylint: disable=unused-argument
3840
name = f"{vector.name}[{index}]"
3941
super().__init__(name)
4042
self._vector = vector
@@ -69,12 +71,16 @@ def __setstate__(self, state):
6971
class ParameterVector:
7072
"""ParameterVector class to quickly generate lists of parameters."""
7173

74+
__slots__ = ("_name", "_params", "_size", "_root_uuid")
75+
7276
def __init__(self, name, length=0):
7377
self._name = name
74-
self._params = []
7578
self._size = length
76-
for i in range(length):
77-
self._params += [ParameterVectorElement(self, i)]
79+
self._root_uuid = uuid4()
80+
root_uuid_int = self._root_uuid.int
81+
self._params = [
82+
ParameterVectorElement(self, i, UUID(int=root_uuid_int + i)) for i in range(length)
83+
]
7884

7985
@property
8086
def name(self):
@@ -119,6 +125,11 @@ def resize(self, length):
119125
This is to ensure that the parameter instances do not change.
120126
"""
121127
if length > len(self._params):
122-
for i in range(len(self._params), length):
123-
self._params += [ParameterVectorElement(self, i)]
128+
root_uuid_int = self._root_uuid.int
129+
self._params.extend(
130+
[
131+
ParameterVectorElement(self, i, UUID(int=root_uuid_int + i))
132+
for i in range(len(self._params), length)
133+
]
134+
)
124135
self._size = length

0 commit comments

Comments
 (0)