Skip to content

Commit ae3c240

Browse files
authored
Update serialising methods and instructions (#951)
* Update serialising methods and instructions * Fix copyright year * Add reno * Fix spelling * Fix spelling * Fix spelling * Fix spelling * Update tutorial 09 and remove upgrade reno * Update removal version * Update removal version
1 parent 198a8ca commit ae3c240

13 files changed

+104
-40
lines changed

.pylintdict

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ deterministically
109109
diag
110110
dicts
111111
diederik
112+
dill
112113
dimensionality
113114
dir
114115
discretization
@@ -119,6 +120,7 @@ disp
119120
distro
120121
dobsicek
121122
docstring
123+
docstrings
122124
doi
123125
dok
124126
dp
@@ -500,6 +502,7 @@ sdg
500502
seealso
501503
semidefinite
502504
serializable
505+
serializablemodelmixin
503506
shalev
504507
shanno
505508
shende

docs/tutorials/09_saving_and_loading_models.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@
535535
"metadata": {},
536536
"outputs": [],
537537
"source": [
538-
"original_classifier.save(\"vqc_classifier.model\")"
538+
"original_classifier.to_dill(\"vqc_classifier.model\")"
539539
]
540540
},
541541
{
@@ -555,7 +555,7 @@
555555
"metadata": {},
556556
"outputs": [],
557557
"source": [
558-
"loaded_classifier = VQC.load(\"vqc_classifier.model\")"
558+
"loaded_classifier = VQC.from_dill(\"vqc_classifier.model\")"
559559
]
560560
},
561561
{

qiskit_machine_learning/algorithms/classifiers/qsvc.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2021, 2024.
3+
# (C) Copyright IBM 2021, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -34,13 +34,24 @@ class QSVC(SVC, SerializableModelMixin):
3434
Read more in the `scikit-learn user guide
3535
<https://scikit-learn.org/stable/modules/svm.html#svm-classification>`_.
3636
37-
**Example**
37+
Examples:
38+
.. code-block::
3839
39-
.. code-block::
40+
from qiskit_machine_learning.kernels import FidelityQuantumKernel
41+
from qiskit_machine_learning.algorithms import QSVC
42+
43+
kernel = FidelityQuantumKernel()
44+
qsvc = QSVC(quantum_kernel=kernel)
45+
qsvc.fit(X_train, y_train)
46+
y_pred = qsvc.predict(X_test)
47+
48+
# Save the trained model
49+
qsvc.to_dill('qsvc_model.dill')
50+
51+
# Load the model for later use
52+
loaded_qsvc = QSVC.from_dill('qsvc_model.dill')
53+
score = loaded_qsvc.score(X_test, y_test)
4054
41-
qsvc = QSVC(quantum_kernel=qkernel)
42-
qsvc.fit(sample_train,label_train)
43-
qsvc.predict(sample_test)
4455
"""
4556

4657
def __init__(self, *, quantum_kernel: Optional[BaseKernel] = None, **kwargs):

qiskit_machine_learning/algorithms/serializable_model.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2021, 2023.
3+
# (C) Copyright IBM 2021, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -15,37 +15,70 @@
1515

1616
import dill
1717

18+
from ..utils.deprecation import issue_deprecation_msg
19+
1820

1921
class SerializableModelMixin:
2022
"""
21-
Provides convenient methods for saving and loading models.
23+
Provides convenient methods for saving and loading models via dill serialization.
24+
25+
.. warning::
26+
The legacy :meth:`save` and :meth:`load` methods are deprecated in v0.9.0
27+
and will be removed in a future release. Please use :meth:`to_dill`
28+
and :meth:`from_dill` respectively.
29+
2230
"""
2331

24-
def save(self, file_name: str) -> None:
32+
def to_dill(self, file_name: str) -> None:
2533
"""
2634
Saves this model to the specified file. Internally, the model is serialized via ``dill``.
2735
All parameters are saved, including a primitive instance that is referenced by internal
2836
objects. That means if a model is loaded from a file and is used, for instance, for
2937
inference, the same primitive will be used even if a cloud primitive was used.
3038
39+
.. warning::
40+
Replaces the deprecated :meth:`save` method.
41+
3142
Args:
32-
file_name: a file name or path where to save the model.
43+
file_name: Path where the serialized model will be written.
44+
45+
Example:
46+
.. code-block::
47+
48+
model.to_dill('model_state.dill')
3349
"""
3450
with open(file_name, "wb") as handler:
3551
dill.dump(self, handler)
3652

53+
def save(self, *args) -> None:
54+
"""Backwards compatibility with :meth:`to_dill`, deprecated in v0.9.0."""
55+
issue_deprecation_msg(
56+
msg="SerializableModelMixin.save() is deprecated.",
57+
version="0.9.0",
58+
remedy="Use the to_dill() method instead.",
59+
period="4 months",
60+
)
61+
self.to_dill(*args)
62+
3763
@classmethod
38-
def load(cls, file_name: str) -> Any:
64+
def from_dill(cls, file_name: str) -> Any:
3965
"""
40-
Loads a model from the file. If the loaded model is not an instance of the class whose
66+
Loads a model from a file. If the loaded model is not an instance of the class whose
4167
method was called, then a warning is raised. Nevertheless, the loaded model may be a valid
4268
model.
4369
70+
Replaces the deprecated :meth:`load` method.
71+
4472
Args:
45-
file_name: a file name or path to load a model from.
73+
file_name: Path to the dill file containing the serialized model.
4674
4775
Returns:
48-
A loaded model.
76+
An instance of the model loaded from disk.
77+
78+
Example:
79+
.. code-block::
80+
81+
loaded = MyModel.from_dill('model_state.dill')
4982
5083
Raises:
5184
TypeError: if a loaded model is not an instance of the expected class.
@@ -55,3 +88,14 @@ def load(cls, file_name: str) -> Any:
5588
if not isinstance(model, cls):
5689
raise TypeError(f"Loaded model is of class {type(model)}. Expected class: {cls}.")
5790
return model
91+
92+
@classmethod
93+
def load(cls, *args) -> Any:
94+
"""Backwards compatibility with :meth:`from_dill`, deprecated in v0.9.0."""
95+
issue_deprecation_msg(
96+
msg="SerializableModelMixin.load() is deprecated.",
97+
version="0.9.0",
98+
remedy="Use the from_dill() classmethod instead.",
99+
period="4 months",
100+
)
101+
return cls.from_dill(*args)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
deprecations:
3+
- |
4+
The methods :meth:`SerializableModelMixin.save` and :meth:`SerializableModelMixin.load` have
5+
been deprecated and will be removed (target v0.10.0). Please use
6+
:meth:`SerializableModelMixin.to_dill` and :meth:`SerializableModelMixin.from_dill` instead.

test/algorithms/classifiers/test_fidelity_quantum_kernel_pegasos_qsvc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,9 @@ def test_save_load(self):
208208

209209
# save/load, change the quantum instance and check if predicted values are the same
210210
file_name = os.path.join(tempfile.gettempdir(), "pegasos.model")
211-
regressor.save(file_name)
211+
regressor.to_dill(file_name)
212212
try:
213-
regressor_load = PegasosQSVC.load(file_name)
213+
regressor_load = PegasosQSVC.from_dill(file_name)
214214
loaded_model_predicts = regressor_load.predict(test_features)
215215

216216
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -222,7 +222,7 @@ class FakeModel(SerializableModelMixin):
222222
pass
223223

224224
with self.assertRaises(TypeError):
225-
FakeModel.load(file_name)
225+
FakeModel.from_dill(file_name)
226226

227227
finally:
228228
os.remove(file_name)

test/algorithms/classifiers/test_fidelity_quantum_kernel_qsvc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ def test_save_load(self):
105105

106106
# save/load, change the quantum instance and check if predicted values are the same
107107
file_name = os.path.join(tempfile.gettempdir(), "qsvc.model")
108-
classifier.save(file_name)
108+
classifier.to_dill(file_name)
109109
try:
110-
classifier_load = QSVC.load(file_name)
110+
classifier_load = QSVC.from_dill(file_name)
111111
loaded_model_predicts = classifier_load.predict(test_features)
112112

113113
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -119,7 +119,7 @@ class FakeModel(SerializableModelMixin):
119119
pass
120120

121121
with self.assertRaises(TypeError):
122-
FakeModel.load(file_name)
122+
FakeModel.from_dill(file_name)
123123

124124
finally:
125125
os.remove(file_name)

test/algorithms/classifiers/test_neural_network_classifier.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,9 @@ def test_save_load(self, qnn_type):
390390
# save/load, change the quantum instance and check if predicted values are the same
391391
with tempfile.TemporaryDirectory() as dir_name:
392392
file_name = os.path.join(dir_name, "classifier.model")
393-
classifier.save(file_name)
393+
classifier.to_dill(file_name)
394394

395-
classifier_load = NeuralNetworkClassifier.load(file_name)
395+
classifier_load = NeuralNetworkClassifier.from_dill(file_name)
396396
loaded_model_predicts = classifier_load.predict(test_features)
397397

398398
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -404,7 +404,7 @@ class FakeModel(SerializableModelMixin):
404404
pass
405405

406406
with self.assertRaises(TypeError):
407-
FakeModel.load(file_name)
407+
FakeModel.from_dill(file_name)
408408

409409
@idata((True, False))
410410
def test_num_classes_data(self, one_hot):

test/algorithms/classifiers/test_pegasos_qsvc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ def test_save_load(self):
227227

228228
# save/load, change the quantum instance and check if predicted values are the same
229229
file_name = os.path.join(tempfile.gettempdir(), "pegasos.model")
230-
regressor.save(file_name)
230+
regressor.to_dill(file_name)
231231
try:
232-
regressor_load = PegasosQSVC.load(file_name)
232+
regressor_load = PegasosQSVC.from_dill(file_name)
233233
loaded_model_predicts = regressor_load.predict(test_features)
234234

235235
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -241,7 +241,7 @@ class FakeModel(SerializableModelMixin):
241241
pass
242242

243243
with self.assertRaises(TypeError):
244-
FakeModel.load(file_name)
244+
FakeModel.from_dill(file_name)
245245

246246
finally:
247247
os.remove(file_name)

test/algorithms/classifiers/test_qsvc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ def test_save_load(self):
107107

108108
# save/load, change the quantum instance and check if predicted values are the same
109109
file_name = os.path.join(tempfile.gettempdir(), "qsvc.model")
110-
classifier.save(file_name)
110+
classifier.to_dill(file_name)
111111
try:
112-
classifier_load = QSVC.load(file_name)
112+
classifier_load = QSVC.from_dill(file_name)
113113
loaded_model_predicts = classifier_load.predict(test_features)
114114

115115
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -121,7 +121,7 @@ class FakeModel(SerializableModelMixin):
121121
pass
122122

123123
with self.assertRaises(TypeError):
124-
FakeModel.load(file_name)
124+
FakeModel.from_dill(file_name)
125125

126126
finally:
127127
os.remove(file_name)

test/algorithms/regressors/test_fidelity_quantum_kernel_qsvr.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ def test_save_load(self):
136136

137137
with tempfile.TemporaryDirectory() as dir_name:
138138
file_name = os.path.join(dir_name, "qsvr.model")
139-
regressor.save(file_name)
139+
regressor.to_dill(file_name)
140140

141-
regressor_load = QSVR.load(file_name)
141+
regressor_load = QSVR.from_dill(file_name)
142142
loaded_model_predicts = regressor_load.predict(test_features)
143143

144144
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -149,7 +149,7 @@ class FakeModel(SerializableModelMixin):
149149
pass
150150

151151
with self.assertRaises(TypeError):
152-
FakeModel.load(file_name)
152+
FakeModel.from_dill(file_name)
153153

154154

155155
if __name__ == "__main__":

test/algorithms/regressors/test_neural_network_regressor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ def test_save_load(self):
189189
# save/load, change the quantum instance and check if predicted values are the same
190190
with tempfile.TemporaryDirectory() as dir_name:
191191
file_name = os.path.join(dir_name, "regressor.model")
192-
regressor.save(file_name)
192+
regressor.to_dill(file_name)
193193

194-
regressor_load = NeuralNetworkRegressor.load(file_name)
194+
regressor_load = NeuralNetworkRegressor.from_dill(file_name)
195195
loaded_model_predicts = regressor_load.predict(test_features)
196196

197197
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -203,7 +203,7 @@ class FakeModel(SerializableModelMixin):
203203
pass
204204

205205
with self.assertRaises(TypeError):
206-
FakeModel.load(file_name)
206+
FakeModel.from_dill(file_name)
207207

208208
def test_untrained(self):
209209
"""Test untrained regressor."""

test/algorithms/regressors/test_qsvr.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ def test_save_load(self):
131131

132132
with tempfile.TemporaryDirectory() as dir_name:
133133
file_name = os.path.join(dir_name, "qsvr.model")
134-
regressor.save(file_name)
134+
regressor.to_dill(file_name)
135135

136-
regressor_load = QSVR.load(file_name)
136+
regressor_load = QSVR.from_dill(file_name)
137137
loaded_model_predicts = regressor_load.predict(test_features)
138138

139139
np.testing.assert_array_almost_equal(original_predicts, loaded_model_predicts)
@@ -144,7 +144,7 @@ class FakeModel(SerializableModelMixin):
144144
pass
145145

146146
with self.assertRaises(TypeError):
147-
FakeModel.load(file_name)
147+
FakeModel.from_dill(file_name)
148148

149149

150150
if __name__ == "__main__":

0 commit comments

Comments
 (0)