Skip to content

Commit 87ae03e

Browse files
committed
Cleaner implementation: use parameter vector ROOT uuid as key, resolve element uuids from root uuid
1 parent 97b70bc commit 87ae03e

File tree

3 files changed

+109
-45
lines changed

3 files changed

+109
-45
lines changed

qiskit/qpy/binary_io/circuits.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ def _loads_instruction_parameter(
167167
circuit,
168168
use_symengine,
169169
standalone_vars,
170-
initialize_full_vec,
171170
):
172171
if type_key == type_keys.Program.CIRCUIT:
173172
param = common.data_from_binary(data_bytes, read_circuit, version=version)
@@ -187,7 +186,6 @@ def _loads_instruction_parameter(
187186
circuit=circuit,
188187
use_symengine=use_symengine,
189188
standalone_vars=standalone_vars,
190-
initialize_full_vec=initialize_full_vec,
191189
)
192190
)
193191
elif type_key == type_keys.Value.INTEGER:
@@ -209,7 +207,6 @@ def _loads_instruction_parameter(
209207
cregs=registers["c"],
210208
use_symengine=use_symengine,
211209
standalone_vars=standalone_vars,
212-
initialize_full_vec=initialize_full_vec,
213210
)
214211

215212
return param
@@ -297,14 +294,6 @@ def _read_instruction(
297294
# Load Parameters
298295
for _param in range(instruction.num_parameters):
299296
type_key, data_bytes = common.read_generic_typed_data(file_obj)
300-
# If the gate is in the list of exceptions (subclasses of BlueprintCircuit),
301-
# and the parameter type is a parameter vector, initialize all elements of
302-
# the vector. This is to avoid unnecessary UserWarnings about elements in
303-
# the non-user-actionable parameter vector not being initialized.
304-
initialize_full_vec = any(
305-
exception in gate_name
306-
for exception in ["ZZFeatureMap", "ZFeatureMap", "PauliFeatureMap"]
307-
)
308297
param = _loads_instruction_parameter(
309298
type_key,
310299
data_bytes,
@@ -314,7 +303,6 @@ def _read_instruction(
314303
circuit,
315304
use_symengine,
316305
standalone_vars,
317-
initialize_full_vec,
318306
)
319307
params.append(param)
320308

qiskit/qpy/binary_io/value.py

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -453,28 +453,28 @@ def _read_parameter(file_obj):
453453
return Parameter(name, uuid=param_uuid)
454454

455455

456-
def _read_parameter_vec(file_obj, vectors, initialize_full_vec):
456+
def _read_parameter_vec(file_obj, vectors):
457457
data = formats.PARAMETER_VECTOR_ELEMENT(
458458
*struct.unpack(
459459
formats.PARAMETER_VECTOR_ELEMENT_PACK,
460460
file_obj.read(formats.PARAMETER_VECTOR_ELEMENT_SIZE),
461461
),
462462
)
463-
# Starting in version 15, the parameter uuid is used instead of the
464-
# parameter name.
465-
param_uuid = uuid.UUID(bytes=data.uuid)
463+
# Starting in version 15, the parameter vector root uuid
464+
# is used as a key instead of the parameter name.
465+
root_uuid_int = uuid.UUID(bytes=data.uuid).int - data.index
466+
root_uuid = uuid.UUID(int=root_uuid_int)
466467
name = file_obj.read(data.vector_name_size).decode(common.ENCODE)
467468

468-
if param_uuid not in vectors:
469-
vectors[param_uuid] = (ParameterVector(name, data.vector_size), set())
470-
vector = vectors[param_uuid][0]
471-
if initialize_full_vec:
472-
for index in range(data.vector_size):
473-
vectors[param_uuid][1].add(index)
474-
vector._params[index] = ParameterVectorElement(vector, index, uuid=param_uuid)
475-
elif vector[data.index].uuid != param_uuid:
476-
vectors[param_uuid][1].add(data.index)
477-
vector._params[data.index] = ParameterVectorElement(vector, data.index, uuid=param_uuid)
469+
if root_uuid not in vectors:
470+
vectors[root_uuid] = (ParameterVector(name, data.vector_size), set())
471+
vector = vectors[root_uuid][0]
472+
473+
if vector[data.index].uuid != root_uuid:
474+
vectors[root_uuid][1].add(data.index)
475+
vector._params[data.index] = ParameterVectorElement(
476+
vector, data.index, uuid=uuid.UUID(int=root_uuid_int + data.index)
477+
)
478478
return vector[data.index]
479479

480480

@@ -540,7 +540,7 @@ def _read_parameter_expression_v3(file_obj, vectors, use_symengine):
540540
symbol = _read_parameter(file_obj)
541541
elif symbol_key == type_keys.Value.PARAMETER_VECTOR:
542542
# If a parameter vector is used within an expression, initialize all elements.
543-
symbol = _read_parameter_vec(file_obj, vectors, initialize_full_vec=True)
543+
symbol = _read_parameter_vec(file_obj, vectors)
544544
else:
545545
raise exceptions.QpyError(f"Invalid parameter expression map type: {symbol_key}")
546546

@@ -589,7 +589,7 @@ def _read_parameter_expression_v13(file_obj, vectors, version):
589589
symbol = _read_parameter(file_obj)
590590
elif symbol_key == type_keys.Value.PARAMETER_VECTOR:
591591
# If a parameter vector is used within an expression, initialize all elements.
592-
symbol = _read_parameter_vec(file_obj, vectors, initialize_full_vec=True)
592+
symbol = _read_parameter_vec(file_obj, vectors)
593593
elif symbol_key == type_keys.Value.PARAMETER_EXPRESSION:
594594
symbol = _read_parameter_expression_v13(file_obj, vectors, version)
595595

@@ -658,7 +658,6 @@ def _read_parameter_expr_v13(buf, symbol_map, version, vectors):
658658
deserializer=loads_value,
659659
version=version,
660660
vectors=vectors,
661-
initialize_full_vec=True,
662661
)
663662
# Starting in version 15, the uuid is used instead of the name
664663
if version < 15:
@@ -1072,7 +1071,6 @@ def loads_value(
10721071
cregs=None,
10731072
use_symengine=False,
10741073
standalone_vars=(),
1075-
initialize_full_vec=False,
10761074
):
10771075
"""Deserialize input binary data to value object.
10781076
@@ -1089,11 +1087,6 @@ def loads_value(
10891087
before setting this option, as it will be required by qpy to deserialize the payload.
10901088
standalone_vars (Sequence[Var]): standalone :class:`.expr.Var` nodes in the order that they
10911089
were declared by the circuit header.
1092-
initialize_full_vec (bool): If True, de-serialized parameter vectors will be considered
1093-
fully initialized independently of whether all elements are currently used the
1094-
circuit or not. This flag is set to True when loading parameter expressions to avoid
1095-
raising unnecessary user warnings.
1096-
10971090
Returns:
10981091
any: Deserialized value object.
10991092
@@ -1124,7 +1117,6 @@ def loads_value(
11241117
binary_data,
11251118
_read_parameter_vec,
11261119
vectors=vectors,
1127-
initialize_full_vec=initialize_full_vec,
11281120
)
11291121
if type_key == type_keys.Value.PARAMETER:
11301122
return common.data_from_binary(binary_data, _read_parameter)

test/python/circuit/test_circuit_load_from_qpy.py

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@
5353
DiagonalGate,
5454
PauliFeatureMap,
5555
ZZFeatureMap,
56+
RealAmplitudes,
5657
pauli_feature_map,
5758
zz_feature_map,
5859
qaoa_ansatz,
60+
real_amplitudes,
5961
)
6062
from qiskit.circuit.annotated_operation import (
6163
AnnotatedOperation,
@@ -888,23 +890,45 @@ def test_op_evolution_gate_suzuki_trotter(self):
888890
self.assertIsInstance(new_evo, PauliEvolutionGate)
889891
self.assertDeprecatedBitProperties(qc, new_circ)
890892

891-
def test_pauli_feature_map(self):
893+
def test_pauli_feature_map_new(self):
892894
"""Regression test for
893895
https://github.com/Qiskit/qiskit/issues/13720."""
894-
# legacy construction
895-
qc = PauliFeatureMap(feature_dimension=5, reps=1)
896+
# new construction
897+
qc = pauli_feature_map(feature_dimension=5, reps=1)
896898
qpy_file = io.BytesIO()
897899
dump(qc, qpy_file)
898900
qpy_file.seek(0)
899-
new_circuit = load(qpy_file)[0]
901+
with warnings.catch_warnings(record=True) as w:
902+
warnings.simplefilter("always")
903+
new_circuit = load(qpy_file)[0]
904+
for warning in w:
905+
self.assertFalse(
906+
re.search(
907+
r"is not fully identical to its pre-serialization state",
908+
str(warning.message),
909+
)
910+
)
900911
self.assertEqual(qc, new_circuit)
901912

902-
# new construction
903-
qc = pauli_feature_map(feature_dimension=5, reps=1)
913+
def test_pauli_feature_map_legacy(self):
914+
"""Regression test for
915+
https://github.com/Qiskit/qiskit/issues/13720."""
916+
# legacy construction
917+
with self.assertWarns(DeprecationWarning):
918+
qc = PauliFeatureMap(feature_dimension=5, reps=1)
904919
qpy_file = io.BytesIO()
905920
dump(qc, qpy_file)
906921
qpy_file.seek(0)
907-
new_circuit = load(qpy_file)[0]
922+
with warnings.catch_warnings(record=True) as w:
923+
warnings.simplefilter("always")
924+
new_circuit = load(qpy_file)[0]
925+
for warning in w:
926+
self.assertFalse(
927+
re.search(
928+
r"is not fully identical to its pre-serialization state",
929+
str(warning.message),
930+
)
931+
)
908932
self.assertEqual(qc, new_circuit)
909933

910934
def test_zz_feature_map_new(self):
@@ -929,7 +953,66 @@ def test_zz_feature_map_new(self):
929953
def test_zz_feature_map_legacy(self):
930954
"""Regression test for
931955
https://github.com/Qiskit/qiskit/issues/14088."""
932-
qc = ZZFeatureMap(2, reps=1)
956+
with self.assertWarns(DeprecationWarning):
957+
qc = ZZFeatureMap(2, reps=1)
958+
qpy_file = io.BytesIO()
959+
dump(qc, qpy_file)
960+
qpy_file.seek(0)
961+
with warnings.catch_warnings(record=True) as w:
962+
warnings.simplefilter("always")
963+
new_circuit = load(qpy_file)[0]
964+
for warning in w:
965+
self.assertFalse(
966+
re.search(
967+
r"is not fully identical to its pre-serialization state",
968+
str(warning.message),
969+
)
970+
)
971+
self.assertEqual(qc, new_circuit)
972+
973+
def test_zz_feature_map_new(self):
974+
"""Regression test for
975+
https://github.com/Qiskit/qiskit/issues/14088."""
976+
qc = zz_feature_map(2, reps=1)
977+
qpy_file = io.BytesIO()
978+
dump(qc, qpy_file)
979+
qpy_file.seek(0)
980+
with warnings.catch_warnings(record=True) as w:
981+
warnings.simplefilter("always")
982+
new_circuit = load(qpy_file)[0]
983+
for warning in w:
984+
self.assertFalse(
985+
re.search(
986+
r"is not fully identical to its pre-serialization state",
987+
str(warning.message),
988+
)
989+
)
990+
self.assertEqual(qc, new_circuit)
991+
992+
def test_real_amplitudes_legacy(self):
993+
"""Regression test for
994+
https://github.com/Qiskit/qiskit/issues/14088."""
995+
with self.assertWarns(DeprecationWarning):
996+
qc = RealAmplitudes(2, reps=1)
997+
qpy_file = io.BytesIO()
998+
dump(qc, qpy_file)
999+
qpy_file.seek(0)
1000+
with warnings.catch_warnings(record=True) as w:
1001+
warnings.simplefilter("always")
1002+
new_circuit = load(qpy_file)[0]
1003+
for warning in w:
1004+
self.assertFalse(
1005+
re.search(
1006+
r"is not fully identical to its pre-serialization state",
1007+
str(warning.message),
1008+
)
1009+
)
1010+
self.assertEqual(qc, new_circuit)
1011+
1012+
def test_real_amplitudes_new(self):
1013+
"""Regression test for
1014+
https://github.com/Qiskit/qiskit/issues/14088."""
1015+
qc = real_amplitudes(2, reps=1)
9331016
qpy_file = io.BytesIO()
9341017
dump(qc, qpy_file)
9351018
qpy_file.seek(0)
@@ -952,7 +1035,8 @@ def test_duplicated_param_name_legacy(self):
9521035
x = ParameterVector("γ", 1)
9531036

9541037
# legacy construction
955-
ansatz = QAOAAnsatz(op, reps=1)
1038+
with self.assertWarns(DeprecationWarning):
1039+
ansatz = QAOAAnsatz(op, reps=1)
9561040
ansatz = ansatz.assign_parameters({ansatz.parameters[1]: x[0]})
9571041
qc = QuantumCircuit(4)
9581042
qc.append(ansatz, range(4))

0 commit comments

Comments
 (0)