diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 8b1a309a96..6d2af206ed 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -164,7 +164,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: '3.12' - name: Install dependencies run: pip install --upgrade setuptools numpy versioneer wheel @@ -204,7 +204,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.11' + python-version: '3.12' - name: Check SDist run: | diff --git a/.github/workflows/slow-tests-issue.yml b/.github/workflows/slow-tests-issue.yml index cb063eefd7..b5a35dc591 100644 --- a/.github/workflows/slow-tests-issue.yml +++ b/.github/workflows/slow-tests-issue.yml @@ -22,7 +22,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: "3.11" + python-version: "3.12" - name: Trigger the script continue-on-error: true working-directory: scripts/slowest_tests diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cbf9d142e3..3760b4e6b7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,7 @@ jobs: if: ${{ needs.changes.outputs.changes == 'true' }} strategy: matrix: - python-version: ["3.11", "3.14"] + python-version: ["3.12", "3.14"] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -78,7 +78,7 @@ jobs: matrix: # default-mode: "NUMBA" is actually a no-op, to make sure we're testing default config settings default-mode: ["CVM", "NUMBA", "FAST_COMPILE"] - python-version: ["3.11", "3.14"] + python-version: ["3.12", "3.14"] os: ["ubuntu-latest"] install-jax: [0] install-torch: [0] @@ -94,7 +94,7 @@ jobs: - [ "tensor rewriting", "tests/tensor/rewriting" ] - [ "tensor linalg", "tests/tensor/linalg" ] exclude: - - python-version: "3.11" + - python-version: "3.12" default-mode: "FAST_COMPILE" include: - part: ["doctests", "--doctest-modules pytensor --ignore=pytensor/misc/check_duplicate_key.py --ignore=pytensor/link --ignore=pytensor/ipython.py"] @@ -117,7 +117,7 @@ jobs: - part: ["pytorch link", "tests/link/pytorch"] install-torch: 1 default-mode: "CVM" - python-version: "3.11" + python-version: "3.12" os: "ubuntu-latest" - part: ["xtensor", "tests/xtensor"] install-xarray: 1 @@ -127,7 +127,7 @@ jobs: - part: ["mlx link", "tests/link/mlx"] install-mlx: 1 default-mode: "CVM" - python-version: "3.11" + python-version: "3.12" os: "macos-15" - part: ["macos smoke test", "tests/tensor/test_elemwise.py tests/tensor/test_math_scipy.py tests/tensor/test_blas.py"] default-mode: "CVM" @@ -223,7 +223,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - name: Set up Python 3.11 + - name: Set up Python 3.12 uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 # v2.0.7 with: environment-name: pytensor-test @@ -239,7 +239,7 @@ jobs: python -c 'import pytensor; print(pytensor.config.__str__(print_doc=False))' python -c 'import pytensor; assert pytensor.config.blas__ldflags != "", "Blas flags are empty"' env: - PYTHON_VERSION: 3.11 + PYTHON_VERSION: 3.12 - name: Download previous benchmark data uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: diff --git a/doc/environment.yml b/doc/environment.yml index 50dd65df6b..8d3419e7a7 100644 --- a/doc/environment.yml +++ b/doc/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge - nodefaults dependencies: - - python=3.11 + - python=3.12 - gcc_linux-64 - gxx_linux-64 - numpy diff --git a/environment-osx-arm64.yml b/environment-osx-arm64.yml index 2424332e86..74d524466b 100644 --- a/environment-osx-arm64.yml +++ b/environment-osx-arm64.yml @@ -7,7 +7,7 @@ name: pytensor-dev channels: - conda-forge dependencies: - - python>=3.11 + - python>=3.12 - compilers - numpy>=2.0.0 - scipy>=1,<2 diff --git a/environment.yml b/environment.yml index 9ca4b350a1..4e28fe7b91 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,7 @@ name: pytensor-dev channels: - conda-forge dependencies: - - python>=3.11 + - python>=3.12 - compilers - numpy>=2.0.0 - scipy>=1,<2 diff --git a/pyproject.toml b/pyproject.toml index 1edd5034ec..ea9d0728f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "pytensor" dynamic = ['version'] -requires-python = ">=3.11,<3.15" +requires-python = ">=3.12,<3.15" authors = [{ name = "pymc-devs", email = "pymc.devs@gmail.com" }] description = "Optimizing compiler for evaluating mathematical expressions on CPUs and GPUs." readme = "README.rst" @@ -30,7 +30,6 @@ classifiers = [ "Operating System :: Unix", "Operating System :: MacOS", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", @@ -169,7 +168,7 @@ lines-after-imports = 2 [tool.mypy] -python_version = "3.11" +python_version = "3.12" ignore_missing_imports = true strict_equality = true warn_redundant_casts = true diff --git a/pytensor/gradient.py b/pytensor/gradient.py index ccc89fffb9..7eb7150fef 100644 --- a/pytensor/gradient.py +++ b/pytensor/gradient.py @@ -48,7 +48,7 @@ def _match_tangent_dtype(var: Variable, tangent: Variable) -> Variable: # TODO: Add `overload` variants -def as_list_or_tuple( +def as_list_or_tuple[V: Variable | None]( use_list: bool, use_tuple: bool, outputs: V | Sequence[V] ) -> V | list[V] | tuple[V, ...]: """Return either a single object or a list/tuple of objects. diff --git a/pytensor/graph/basic.py b/pytensor/graph/basic.py index 32601cdb3e..b58c8635c9 100644 --- a/pytensor/graph/basic.py +++ b/pytensor/graph/basic.py @@ -108,7 +108,7 @@ def dprint(self, **kwargs): return debugprint(self, **kwargs) -class Apply(Node, Generic[OpType]): +class Apply(Node, Generic[OpType]): # noqa: UP046 """A `Node` representing the application of an operation to inputs. Basically, an `Apply` instance is an object that represents the @@ -345,7 +345,7 @@ def params_type(self): return self.op.params_type -class Variable(Node, Generic[_TypeType, OptionalApplyType]): +class Variable(Node, Generic[_TypeType, OptionalApplyType]): # noqa: UP046 r""" A :term:`Variable` is a node in an expression graph that represents a variable. @@ -677,7 +677,7 @@ def clone(self, **kwargs): return cp -class NominalVariable(Generic[_TypeType, _IdType], AtomicVariable[_TypeType]): +class NominalVariable(Generic[_TypeType, _IdType], AtomicVariable[_TypeType]): # noqa: UP046 """A variable that enables alpha-equivalent comparisons.""" __instances__: dict[tuple["Type", Hashable], "NominalVariable"] = {} diff --git a/pytensor/graph/rewriting/unify.py b/pytensor/graph/rewriting/unify.py index 195dd18564..c0b48f8aad 100644 --- a/pytensor/graph/rewriting/unify.py +++ b/pytensor/graph/rewriting/unify.py @@ -14,7 +14,7 @@ from dataclasses import dataclass from numbers import Number from types import UnionType -from typing import Any, TypeAlias +from typing import Any import numpy as np from cons.core import ConsError, _car, _cdr @@ -262,7 +262,7 @@ class LiteralString: value: str -OpPatternOpTypeType: TypeAlias = type[Op] | tuple[type[Op], ...] | UnionType +type OpPatternOpTypeType = type[Op] | tuple[type[Op], ...] | UnionType @dataclass(unsafe_hash=True) diff --git a/pytensor/graph/traversal.py b/pytensor/graph/traversal.py index 08b0bc2fd8..fd18b0a2b4 100644 --- a/pytensor/graph/traversal.py +++ b/pytensor/graph/traversal.py @@ -20,7 +20,7 @@ @overload -def walk( +def walk[T: Node]( nodes: Iterable[T], expand: Callable[[T], Iterable[T] | None], bfs: bool = True, @@ -29,7 +29,7 @@ def walk( @overload -def walk( +def walk[T: Node]( nodes: Iterable[T], expand: Callable[[T], Iterable[T] | None], bfs: bool, @@ -37,7 +37,7 @@ def walk( ) -> Generator[NodeAndChildren, None, None]: ... -def walk( +def walk[T: Node]( nodes: Iterable[T], expand: Callable[[T], Iterable[T] | None], bfs: bool = True, @@ -511,7 +511,7 @@ def truncated_graph_inputs( return truncated_inputs -def walk_toposort( +def walk_toposort[T: Node]( graphs: Iterable[T], deps: Callable[[T], Iterable[T] | None], ) -> Generator[T, None, None]: @@ -579,7 +579,7 @@ def compute_deps_cache(obj, deps_cache=deps_cache): raise ValueError("graph contains cycles") -def general_toposort( +def general_toposort[T: Node]( outputs: Iterable[T], deps: Callable[[T], Iterable[T] | None], compute_deps_cache: Callable[[T], Iterable[T] | None] | None = None, diff --git a/pytensor/graph/type.py b/pytensor/graph/type.py index d4d800716d..a9a961564c 100644 --- a/pytensor/graph/type.py +++ b/pytensor/graph/type.py @@ -9,7 +9,7 @@ D = TypeVar("D") -class Type(MetaObject, Generic[D]): +class Type(MetaObject, Generic[D]): # noqa: UP046 """ Interface specification for variable type instances. @@ -23,12 +23,12 @@ class Type(MetaObject, Generic[D]): """ - variable_type: TypeAlias = Variable + variable_type: TypeAlias = Variable # noqa: UP040 """ The `Type` that will be created by a call to `Type.make_variable`. """ - constant_type: TypeAlias = Constant + constant_type: TypeAlias = Constant # noqa: UP040 """ The `Type` that will be created by a call to `Type.make_constant`. """ diff --git a/pytensor/graph/utils.py b/pytensor/graph/utils.py index 8e8eb440d2..d24003230b 100644 --- a/pytensor/graph/utils.py +++ b/pytensor/graph/utils.py @@ -73,7 +73,7 @@ def simple_extract_stack( return trace -def add_tag_trace(thing: T, user_line: int | None = None) -> T: +def add_tag_trace[T: "Apply" | "Variable"](thing: T, user_line: int | None = None) -> T: """Add tag.trace to a node or variable. The argument is returned after being affected (inplace). diff --git a/pytensor/link/numba/dispatch/linalg/solvers/lu_solve.py b/pytensor/link/numba/dispatch/linalg/solvers/lu_solve.py index 0939807fd8..b737e9d5d6 100644 --- a/pytensor/link/numba/dispatch/linalg/solvers/lu_solve.py +++ b/pytensor/link/numba/dispatch/linalg/solvers/lu_solve.py @@ -1,5 +1,5 @@ from collections.abc import Callable -from typing import Literal, TypeAlias +from typing import Literal import numpy as np from numba.core.extending import overload @@ -21,7 +21,7 @@ ) -_Trans: TypeAlias = Literal[0, 1, 2] +type _Trans = Literal[0, 1, 2] def _getrs( diff --git a/pytensor/scalar/basic.py b/pytensor/scalar/basic.py index d3a7f2f650..42f1545026 100644 --- a/pytensor/scalar/basic.py +++ b/pytensor/scalar/basic.py @@ -15,7 +15,7 @@ from collections.abc import Callable from itertools import chain from textwrap import dedent -from typing import Any, TypeAlias +from typing import Any import numpy as np @@ -790,7 +790,7 @@ def get_scalar_type(dtype, cache: dict[str, ScalarType] = {}) -> ScalarType: complex64: ScalarType = get_scalar_type("complex64") complex128: ScalarType = get_scalar_type("complex128") -_ScalarTypes: TypeAlias = tuple[ScalarType, ...] +type _ScalarTypes = tuple[ScalarType, ...] int_types: _ScalarTypes = (int8, int16, int32, int64) uint_types: _ScalarTypes = (uint8, uint16, uint32, uint64) float_types: _ScalarTypes = (float16, float32, float64) diff --git a/pytensor/tensor/random/variable.py b/pytensor/tensor/random/variable.py index 136322bf27..b7eb931b54 100644 --- a/pytensor/tensor/random/variable.py +++ b/pytensor/tensor/random/variable.py @@ -1,7 +1,6 @@ import copy import warnings from functools import wraps -from typing import TypeAlias import numpy as np @@ -12,7 +11,7 @@ from pytensor.tensor.variable import TensorVariable -RNG_AND_DRAW: TypeAlias = tuple["RandomGeneratorVariable", TensorVariable] +type RNG_AND_DRAW = tuple["RandomGeneratorVariable", TensorVariable] def warn_reuse(func): diff --git a/pytensor/tensor/reshape.py b/pytensor/tensor/reshape.py index b25308fccc..aab187a4ef 100644 --- a/pytensor/tensor/reshape.py +++ b/pytensor/tensor/reshape.py @@ -1,6 +1,5 @@ from collections.abc import Iterable, Sequence from itertools import pairwise -from typing import TypeAlias import numpy as np from numpy.lib._array_utils_impl import normalize_axis_index, normalize_axis_tuple @@ -16,9 +15,7 @@ from pytensor.tensor.variable import TensorVariable -ShapeValueType: TypeAlias = ( - int | np.integer | ScalarVariable | TensorVariable | np.ndarray -) +type ShapeValueType = int | np.integer | ScalarVariable | TensorVariable | np.ndarray class JoinDims(Op): diff --git a/pytensor/tensor/subtensor.py b/pytensor/tensor/subtensor.py index f180d2e9c6..70fbc9fc12 100644 --- a/pytensor/tensor/subtensor.py +++ b/pytensor/tensor/subtensor.py @@ -71,7 +71,7 @@ T = TypeVar("T") -def flatten_index_variables( +def flatten_index_variables[T]( idx_vars: Sequence[T | None | slice], ) -> tuple[list[int | slice], list[T]]: counter = 0 @@ -99,7 +99,7 @@ def flatten_index_variables( return idx_list, flat_vars -def unflatten_index_variables( +def unflatten_index_variables[T]( flat_indices: Sequence[T], idx_list: Sequence[slice | int], ) -> tuple[slice | T, ...]: diff --git a/pytensor/xtensor/random/variable.py b/pytensor/xtensor/random/variable.py index e511753194..5f84f85881 100644 --- a/pytensor/xtensor/random/variable.py +++ b/pytensor/xtensor/random/variable.py @@ -1,5 +1,4 @@ import copy -from typing import TypeAlias import numpy as np @@ -17,7 +16,7 @@ from pytensor.xtensor.type import XTensorVariable -XRNG_AND_DRAW: TypeAlias = tuple["XRandomGeneratorVariable", XTensorVariable] +type XRNG_AND_DRAW = tuple["XRandomGeneratorVariable", XTensorVariable] __all__ = [ "XRandomGeneratorSharedVariable",