Skip to content

Commit

Permalink
Do not retain and expose old elements of ParameterVector (#12561)
Browse files Browse the repository at this point in the history
* Do not retain and expose old elements of `ParameterVector`

This fixes #12541 according to
#12545 (review).

* Fix test
  • Loading branch information
garrison committed Jun 13, 2024
1 parent 8d2144c commit 8a1bcc2
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 21 deletions.
38 changes: 22 additions & 16 deletions qiskit/circuit/parametervector.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ def __setstate__(self, state):
class ParameterVector:
"""ParameterVector class to quickly generate lists of parameters."""

__slots__ = ("_name", "_params", "_size", "_root_uuid")
__slots__ = ("_name", "_params", "_root_uuid")

def __init__(self, name, length=0):
self._name = name
self._size = length
self._root_uuid = uuid4()
root_uuid_int = self._root_uuid.int
self._params = [
Expand All @@ -76,32 +75,38 @@ def index(self, value):
return self._params.index(value)

def __getitem__(self, key):
if isinstance(key, slice):
start, stop, step = key.indices(self._size)
return self.params[start:stop:step]

if key > self._size:
raise IndexError(f"Index out of range: {key} > {self._size}")
return self.params[key]

def __iter__(self):
return iter(self.params[: self._size])
return iter(self.params)

def __len__(self):
return self._size
return len(self._params)

def __str__(self):
return f"{self.name}, {[str(item) for item in self.params[: self._size]]}"
return f"{self.name}, {[str(item) for item in self.params]}"

def __repr__(self):
return f"{self.__class__.__name__}(name={self.name}, length={len(self)})"

def resize(self, length):
"""Resize the parameter vector.
If necessary, new elements are generated. If length is smaller than before, the
previous elements are cached and not re-generated if the vector is enlarged again.
"""Resize the parameter vector. If necessary, new elements are generated.
Note that the UUID of each :class:`.Parameter` element will be generated
deterministically given the root UUID of the ``ParameterVector`` and the index
of the element. In particular, if a ``ParameterVector`` is resized to
be smaller and then later resized to be larger, the UUID of the later
generated element at a given index will be the same as the UUID of the
previous element at that index.
This is to ensure that the parameter instances do not change.
>>> from qiskit.circuit import ParameterVector
>>> pv = ParameterVector("theta", 20)
>>> elt_19 = pv[19]
>>> rv.resize(10)
>>> rv.resize(20)
>>> pv[19] == elt_19
True
"""
if length > len(self._params):
root_uuid_int = self._root_uuid.int
Expand All @@ -111,4 +116,5 @@ def resize(self, length):
for i in range(len(self._params), length)
]
)
self._size = length
else:
del self._params[length:]
2 changes: 1 addition & 1 deletion qiskit/qpy/binary_io/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _write_parameter_vec(file_obj, obj):
struct.pack(
formats.PARAMETER_VECTOR_ELEMENT_PACK,
len(name_bytes),
obj._vector._size,
len(obj._vector),
obj.uuid.bytes,
obj._index,
)
Expand Down
6 changes: 2 additions & 4 deletions test/python/circuit/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1368,10 +1368,8 @@ def test_parametervector_resize(self):
with self.subTest("enlargen"):
vec.resize(3)
self.assertEqual(len(vec), 3)
# ensure we still have the same instance not a copy with the same name
# this is crucial for adding parameters to circuits since we cannot use the same
# name if the instance is not the same
self.assertIs(element, vec[1])
# ensure we still have an element with the same uuid
self.assertEqual(element, vec[1])
self.assertListEqual([param.name for param in vec], _paramvec_names("x", 3))

def test_raise_if_sub_unknown_parameters(self):
Expand Down

0 comments on commit 8a1bcc2

Please sign in to comment.