Skip to content

Commit 5165746

Browse files
authored
Add python 3.13 support (#923)
1 parent 4b918b0 commit 5165746

File tree

7 files changed

+78
-21
lines changed

7 files changed

+78
-21
lines changed

.github/workflows/main.yml

+14-10
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
@@ -112,17 +112,17 @@ jobs:
112112
fail-fast: false
113113
matrix:
114114
os: [ubuntu-latest]
115-
python-version: [3.9, '3.10', 3.11, 3.12]
115+
python-version: [3.9, '3.10', 3.11, 3.12, 3.13]
116116
include:
117117
# macos-latest is an Arm64 image
118118
- os: macos-latest
119119
python-version: 3.9
120120
- os: macos-latest
121-
python-version: 3.12
121+
python-version: 3.13
122122
- os: windows-latest
123123
python-version: 3.9
124124
- os: windows-latest
125-
python-version: 3.12
125+
python-version: 3.13
126126
steps:
127127
- uses: actions/checkout@v4
128128
- uses: actions/setup-python@v5
@@ -184,7 +184,7 @@ jobs:
184184
fail-fast: false
185185
matrix:
186186
os: [ubuntu-latest]
187-
python-version: [3.9, 3.12]
187+
python-version: [3.9, 3.13]
188188
steps:
189189
- uses: actions/checkout@v4
190190
with:
@@ -282,28 +282,32 @@ jobs:
282282
with:
283283
name: ubuntu-latest-3.12
284284
path: /tmp/u312
285+
- uses: actions/download-artifact@v4
286+
with:
287+
name: ubuntu-latest-3.13
288+
path: /tmp/u313
285289
- uses: actions/download-artifact@v4
286290
with:
287291
name: macos-latest-3.9
288292
path: /tmp/m39
289293
- uses: actions/download-artifact@v4
290294
with:
291-
name: macos-latest-3.12
292-
path: /tmp/m312
295+
name: macos-latest-3.13
296+
path: /tmp/m313
293297
- uses: actions/download-artifact@v4
294298
with:
295299
name: windows-latest-3.9
296300
path: /tmp/w39
297301
- uses: actions/download-artifact@v4
298302
with:
299-
name: windows-latest-3.12
300-
path: /tmp/w312
303+
name: windows-latest-3.13
304+
path: /tmp/w313
301305
- name: Install Dependencies
302306
run: pip install -U coverage coveralls diff-cover
303307
shell: bash
304308
- name: Combined Deprecation Messages
305309
run: |
306-
sort -f -u /tmp/u39/ml.dep /tmp/u310/ml.dep /tmp/u311/ml.dep /tmp/u312/ml.dep /tmp/m39/ml.dep /tmp/m312/ml.dep /tmp/w39/ml.dep /tmp/w312/ml.dep || true
310+
sort -f -u /tmp/u39/ml.dep /tmp/u310/ml.dep /tmp/u311/ml.dep /tmp/u312/ml.dep /tmp/u313/ml.dep /tmp/m39/ml.dep /tmp/m313/ml.dep /tmp/w39/ml.dep /tmp/w313/ml.dep || true
307311
shell: bash
308312
- name: Coverage combine
309313
run: coverage3 combine /tmp/u39/ml.dat

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[tool.black]
66
line-length = 100
77

8-
target-version = ['py39', 'py310', 'py311', 'py312']
8+
target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
99

1010
[tool.pylint.main]
1111
extension-pkg-allow-list = [

qiskit_machine_learning/optimizers/gradient_descent.py

+21-5
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
@@ -28,7 +28,7 @@
2828
class GradientDescentState(OptimizerState):
2929
"""State of :class:`~.GradientDescent`.
3030
31-
Dataclass with all the information of an optimizer plus the learning_rate and the stepsize.
31+
Dataclass with all the information of an optimizer plus the learning_rate and the step-size.
3232
"""
3333

3434
stepsize: float | None
@@ -42,6 +42,22 @@ class GradientDescentState(OptimizerState):
4242
4343
"""
4444

45+
# See parent class for a comment on having custom equals. I needed this
46+
# too as it does not appear to use super by default and without this failed
47+
# the exact same way. Note it does not include learning rate as that field
48+
# is not included in the compare as per the field decorator. The __eq__
49+
# method is supposed to accept any object. If you update the version of
50+
# mypy you're using, it'll print out a note recommending this code
51+
# structure.
52+
def __eq__(self, other: object) -> bool:
53+
if not isinstance(other, GradientDescentState):
54+
# If we return NotImplemented, Python will automatically try
55+
# running other.__eq__(self), in case 'other' knows what to do with
56+
# Person objects.
57+
return NotImplemented
58+
59+
return super().__eq__(other) and self.stepsize == other.stepsize
60+
4561

4662
class GradientDescent(SteppableOptimizer):
4763
r"""The gradient descent minimization routine.
@@ -88,7 +104,7 @@ def f(x):
88104
"of {result.fun} using {result.nfev} evaluations.")
89105
90106
An example where the learning rate is an iterator and we supply the analytic gradient.
91-
Note how much faster this convergences (i.e. less ``nfev``) compared to the previous
107+
Note how much faster this converges (i.e. less ``nfev``) compared to the previous
92108
example.
93109
94110
.. code-block:: python
@@ -121,7 +137,7 @@ def grad_f(x):
121137
"of {result.fun} using {result.nfev} evaluations.")
122138
123139
124-
An other example where the evaluation of the function has a chance of failing. The user, with
140+
Another example where the evaluation of the function has a chance of failing. The user, with
125141
specific knowledge about his function can catch this errors and handle them before passing the
126142
result to the optimizer.
127143
@@ -227,7 +243,7 @@ def state(self, state: GradientDescentState) -> None:
227243
def tol(self) -> float:
228244
"""Returns the tolerance of the optimizer.
229245
230-
Any step with smaller stepsize than this value will stop the optimization."""
246+
Any step with smaller step-size than this value will stop the optimization."""
231247
return self._tol
232248

233249
@tol.setter

qiskit_machine_learning/optimizers/steppable_optimizer.py

+32-3
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 2022, 2024.
3+
# (C) Copyright IBM 2022, 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
@@ -73,6 +73,35 @@ class OptimizerState:
7373
nit: int | None
7474
"""Number of optimization steps performed so far in the optimization."""
7575

76+
# Under Python 3.13 the auto-generated equal fails with an error around
77+
# using numpy all or any. See https://github.com/qiskit-community/qiskit-algorithms/pull/225
78+
# for further information. Hence, this custom function was added. The __eq__
79+
# method is supposed to accept any object. If you update the version of
80+
# mypy you're using, it'll print out a note recommending this code structure.
81+
def __eq__(self, other: object) -> bool:
82+
if not isinstance(other, OptimizerState):
83+
# If we return NotImplemented, Python will automatically try
84+
# running other.__eq__(self), in case 'other' knows what to do with
85+
# Person objects.
86+
return NotImplemented
87+
88+
return (
89+
(
90+
self.x == other.x
91+
if isinstance(self.x, float) and isinstance(other.x, float)
92+
else (
93+
False
94+
if isinstance(self.x, float) or isinstance(other.x, float)
95+
else self.x.shape == other.x.shape and (self.x == other.x).all()
96+
)
97+
)
98+
and self.fun == other.fun
99+
and self.jac == other.jac
100+
and self.nfev == other.nfev
101+
and self.njev == other.njev
102+
and self.nit == other.nit
103+
)
104+
76105

77106
class SteppableOptimizer(Optimizer):
78107
"""
@@ -177,7 +206,7 @@ def ask(self) -> AskData:
177206
This method asks the optimizer which are the next points to evaluate.
178207
These points can, e.g., correspond to function values and/or its derivative.
179208
It may also correspond to variables that let the user infer which points to evaluate.
180-
It is the first method inside of a :meth:`~.step` in the optimization process.
209+
It is the first method inside a :meth:`~.step` in the optimization process.
181210
182211
Returns:
183212
An object containing the data needed to make the function evaluation to advance the
@@ -260,7 +289,7 @@ def minimize(
260289
) -> OptimizerResult:
261290
"""Minimizes the function.
262291
263-
For well behaved functions the user can call this method to minimize a function.
292+
For well-behaved functions the user can call this method to minimize a function.
264293
If the user wants more control on how to evaluate the function a custom loop can be
265294
created using :meth:`~.ask` and :meth:`~.tell` and evaluating the function manually.
266295
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Added support for using Qiskit Machine Learning with Python 3.13 following the release of
5+
Python 3.13 (final) in October 2024 (`PEP 719 <https://peps.python.org/pep-0719/>`__). To
6+
access the latest features of Python 3.13, you may update your Qiskit Machine Learning
7+
environment.

setup.py

+2-1
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
@@ -65,6 +65,7 @@
6565
"Programming Language :: Python :: 3.10",
6666
"Programming Language :: Python :: 3.11",
6767
"Programming Language :: Python :: 3.12",
68+
"Programming Language :: Python :: 3.13",
6869
"Topic :: Scientific/Engineering",
6970
],
7071
keywords="qiskit sdk quantum machine learning ml",

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tox]
22
# Sets this min.version because of differences with env_tmp_dir env.
33
minversion = 4.0.2
4-
envlist = py39, py310, py311, py312, lint, gpu, gpu-amd
4+
envlist = py39, py310, py311, py312, py313, lint, gpu, gpu-amd
55
skipsdist = True
66

77
[testenv]

0 commit comments

Comments
 (0)