pytensor-ml 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. pytensor_ml-0.0.6/.github/workflows/mypy.yml +57 -0
  2. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.github/workflows/release.yml +1 -1
  3. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.pre-commit-config.yaml +2 -2
  4. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/PKG-INFO +8 -6
  5. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/conda_envs/pytensor_ml-gpu_jax.yml +1 -2
  6. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/conda_envs/pytensor_ml.yml +8 -2
  7. pytensor_ml-0.0.6/examples/mnist_feed_forward.ipynb +198 -0
  8. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/pyproject.toml +26 -6
  9. pytensor_ml-0.0.6/pytensor_ml/__init__.py +29 -0
  10. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/pytensor_ml/_version.py +2 -2
  11. pytensor_ml-0.0.6/pytensor_ml/activations.py +225 -0
  12. pytensor_ml-0.0.6/pytensor_ml/base.py +56 -0
  13. pytensor_ml-0.0.6/pytensor_ml/checkpoint.py +115 -0
  14. pytensor_ml-0.0.6/pytensor_ml/dispatch/__init__.py +53 -0
  15. pytensor_ml-0.0.6/pytensor_ml/dispatch/jax/__init__.py +3 -0
  16. pytensor_ml-0.0.6/pytensor_ml/dispatch/jax/attention.py +25 -0
  17. pytensor_ml-0.0.6/pytensor_ml/dispatch/mlx/__init__.py +3 -0
  18. pytensor_ml-0.0.6/pytensor_ml/dispatch/mlx/attention.py +30 -0
  19. pytensor_ml-0.0.6/pytensor_ml/json_serialize.py +89 -0
  20. pytensor_ml-0.0.6/pytensor_ml/layers/__init__.py +40 -0
  21. pytensor_ml-0.0.6/pytensor_ml/layers/attention.py +252 -0
  22. pytensor_ml-0.0.6/pytensor_ml/layers/combinators.py +35 -0
  23. pytensor_ml-0.0.6/pytensor_ml/layers/dropout.py +40 -0
  24. pytensor_ml-0.0.6/pytensor_ml/layers/embedding.py +53 -0
  25. pytensor_ml-0.0.6/pytensor_ml/layers/linear.py +58 -0
  26. pytensor_ml-0.0.6/pytensor_ml/layers/norm.py +337 -0
  27. pytensor_ml-0.0.6/pytensor_ml/layers/transformer.py +173 -0
  28. pytensor_ml-0.0.6/pytensor_ml/loss.py +113 -0
  29. pytensor_ml-0.0.6/pytensor_ml/model.py +118 -0
  30. pytensor_ml-0.0.6/pytensor_ml/optim/__init__.py +59 -0
  31. pytensor_ml-0.0.6/pytensor_ml/optim/alias.py +233 -0
  32. pytensor_ml-0.0.6/pytensor_ml/optim/base.py +198 -0
  33. pytensor_ml-0.0.6/pytensor_ml/optim/clipping.py +64 -0
  34. pytensor_ml-0.0.6/pytensor_ml/optim/rules.py +586 -0
  35. pytensor_ml-0.0.6/pytensor_ml/optim/train.py +63 -0
  36. pytensor_ml-0.0.6/pytensor_ml/optim/transform.py +138 -0
  37. pytensor_ml-0.0.6/pytensor_ml/params.py +56 -0
  38. pytensor_ml-0.0.6/pytensor_ml/pretrained.py +256 -0
  39. pytensor_ml-0.0.6/pytensor_ml/pytensorf/__init__.py +29 -0
  40. pytensor_ml-0.0.6/pytensor_ml/pytensorf/collect.py +95 -0
  41. pytensor_ml-0.0.6/pytensor_ml/pytensorf/compile.py +109 -0
  42. pytensor_ml-0.0.6/pytensor_ml/pytensorf/rewrite.py +41 -0
  43. pytensor_ml-0.0.6/pytensor_ml/pytensorf/rng.py +173 -0
  44. pytensor_ml-0.0.6/pytensor_ml/rewriting/layers.py +85 -0
  45. pytensor_ml-0.0.6/pytensor_ml/serialize/__init__.py +2 -0
  46. pytensor_ml-0.0.6/pytensor_ml/serialize/base.py +282 -0
  47. pytensor_ml-0.0.6/pytensor_ml/serialize/blockwise.py +15 -0
  48. pytensor_ml-0.0.6/pytensor_ml/serialize/builders.py +48 -0
  49. pytensor_ml-0.0.6/pytensor_ml/serialize/elemwise.py +36 -0
  50. pytensor_ml-0.0.6/pytensor_ml/serialize/scalar.py +56 -0
  51. pytensor_ml-0.0.6/pytensor_ml/serialize/scan.py +41 -0
  52. pytensor_ml-0.0.6/pytensor_ml/state.py +118 -0
  53. pytensor_ml-0.0.6/pytensor_ml/util.py +75 -0
  54. pytensor_ml-0.0.6/scripts/run_mypy.py +12 -0
  55. pytensor_ml-0.0.6/tests/dispatch/jax/__init__.py +0 -0
  56. pytensor_ml-0.0.6/tests/dispatch/jax/test_attention.py +64 -0
  57. pytensor_ml-0.0.6/tests/dispatch/jax/test_basic.py +75 -0
  58. pytensor_ml-0.0.6/tests/dispatch/mlx/__init__.py +0 -0
  59. pytensor_ml-0.0.6/tests/dispatch/mlx/test_attention.py +65 -0
  60. pytensor_ml-0.0.6/tests/dispatch/mlx/test_basic.py +75 -0
  61. pytensor_ml-0.0.6/tests/optim/__init__.py +0 -0
  62. pytensor_ml-0.0.6/tests/optim/conftest.py +40 -0
  63. pytensor_ml-0.0.6/tests/optim/test_clipping.py +43 -0
  64. pytensor_ml-0.0.6/tests/optim/test_rules.py +439 -0
  65. pytensor_ml-0.0.6/tests/optim/test_training.py +158 -0
  66. pytensor_ml-0.0.6/tests/optim/test_transform.py +86 -0
  67. pytensor_ml-0.0.6/tests/rewriting/__init__.py +0 -0
  68. pytensor_ml-0.0.6/tests/rewriting/conftest.py +23 -0
  69. pytensor_ml-0.0.6/tests/rewriting/test_layers.py +52 -0
  70. pytensor_ml-0.0.6/tests/test_activations.py +143 -0
  71. pytensor_ml-0.0.6/tests/test_attention.py +243 -0
  72. pytensor_ml-0.0.6/tests/test_checkpoint.py +151 -0
  73. pytensor_ml-0.0.6/tests/test_collect.py +82 -0
  74. pytensor_ml-0.0.6/tests/test_layers.py +332 -0
  75. pytensor_ml-0.0.6/tests/test_loss.py +72 -0
  76. pytensor_ml-0.0.6/tests/test_model.py +102 -0
  77. pytensor_ml-0.0.6/tests/test_pretrained.py +142 -0
  78. pytensor_ml-0.0.6/tests/test_pytensorf.py +42 -0
  79. pytensor_ml-0.0.6/tests/test_serialize.py +262 -0
  80. pytensor_ml-0.0.6/tests/test_state.py +93 -0
  81. pytensor_ml-0.0.6/tests/test_transformer.py +142 -0
  82. pytensor_ml-0.0.6/tests/test_util.py +93 -0
  83. pytensor_ml-0.0.4/examples/mnist_feed_forward.ipynb +0 -359
  84. pytensor_ml-0.0.4/pytensor_ml/__init__.py +0 -16
  85. pytensor_ml-0.0.4/pytensor_ml/activations.py +0 -57
  86. pytensor_ml-0.0.4/pytensor_ml/layers.py +0 -238
  87. pytensor_ml-0.0.4/pytensor_ml/loss.py +0 -65
  88. pytensor_ml-0.0.4/pytensor_ml/model.py +0 -109
  89. pytensor_ml-0.0.4/pytensor_ml/optimizers.py +0 -365
  90. pytensor_ml-0.0.4/pytensor_ml/params.py +0 -226
  91. pytensor_ml-0.0.4/pytensor_ml/pytensorf.py +0 -115
  92. pytensor_ml-0.0.4/pytensor_ml/rewriting/layers.py +0 -85
  93. pytensor_ml-0.0.4/pytensor_ml/state.py +0 -303
  94. pytensor_ml-0.0.4/pytensor_ml/util.py +0 -49
  95. pytensor_ml-0.0.4/tests/rewriting/test_layers.py +0 -62
  96. pytensor_ml-0.0.4/tests/test_layers.py +0 -182
  97. pytensor_ml-0.0.4/tests/test_loss.py +0 -61
  98. pytensor_ml-0.0.4/tests/test_model.py +0 -101
  99. pytensor_ml-0.0.4/tests/test_optimizers.py +0 -172
  100. pytensor_ml-0.0.4/tests/test_params.py +0 -107
  101. pytensor_ml-0.0.4/tests/test_state.py +0 -138
  102. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.gitattributes +0 -0
  103. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.github/workflows/pre-commit.yml +0 -0
  104. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.github/workflows/run_tests.yml +0 -0
  105. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.gitignore +0 -0
  106. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/LICENSE +0 -0
  107. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/README.md +0 -0
  108. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/pytensor_ml/rewriting/__init__.py +0 -0
  109. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/tests/__init__.py +0 -0
  110. {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/tests/conftest.py +0 -0
  111. {pytensor_ml-0.0.4/tests/rewriting → pytensor_ml-0.0.6/tests/dispatch}/__init__.py +0 -0
  112. /pytensor_ml-0.0.4/tests/test_pytensorf.py → /pytensor_ml-0.0.6/tests/test_base.py +0 -0
@@ -0,0 +1,57 @@
1
+ name: mypy
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+ paths:
8
+ - ".github/workflows/mypy.yml"
9
+ - "pytensor_ml/**"
10
+ - "scripts/run_mypy.py"
11
+ - "pyproject.toml"
12
+ - "conda_envs/**"
13
+
14
+ # Cancels all previous workflow runs for pull requests that have not completed.
15
+ concurrency:
16
+ group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ mypy:
21
+ runs-on: ubuntu-latest
22
+
23
+ defaults:
24
+ run:
25
+ shell: bash -l {0}
26
+
27
+ steps:
28
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29
+ - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
30
+ env:
31
+ CACHE_NUMBER: 0
32
+ with:
33
+ path: ~/conda_pkgs_dir
34
+ key: ${{ runner.os }}-py312-conda-${{ env.CACHE_NUMBER }}-${{
35
+ hashFiles('conda_envs/pytensor_ml.yml') }}
36
+ - uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1
37
+ with:
38
+ miniforge-variant: Miniforge3
39
+ miniforge-version: latest
40
+ mamba-version: "*"
41
+ activate-environment: pytensor_ml
42
+ channel-priority: strict
43
+ environment-file: conda_envs/pytensor_ml.yml
44
+ python-version: 3.12
45
+ use-mamba: true
46
+ use-only-tar-bz2: false
47
+
48
+ - name: Install current branch
49
+ run: |
50
+ conda activate pytensor_ml
51
+ pip install -e .
52
+ python --version
53
+
54
+ - name: Run mypy
55
+ run: |
56
+ conda activate pytensor_ml
57
+ python ./scripts/run_mypy.py
@@ -30,7 +30,7 @@ jobs:
30
30
 
31
31
  publish-package:
32
32
  # Don't publish from forks
33
- if: github.repository_owner == 'jessegrabowski' && github.event_name == 'release' && github.event.action == 'published'
33
+ if: github.repository_owner == 'pymc-devs' && github.event_name == 'release' && github.event.action == 'published'
34
34
  # Use the `release` GitHub environment to protect the Trusted Publishing (OIDC)
35
35
  # workflow by requiring signoff from a maintainer.
36
36
  environment: release
@@ -12,7 +12,7 @@ repos:
12
12
  - id: trailing-whitespace
13
13
 
14
14
  - repo: https://github.com/astral-sh/ruff-pre-commit
15
- rev: v0.15.15
15
+ rev: v0.15.17
16
16
  hooks:
17
17
  - id: ruff
18
18
  args: [ --fix, --exit-non-zero-on-fix ]
@@ -50,7 +50,7 @@ repos:
50
50
  conf.py)
51
51
  entry: >
52
52
  (?x)(arviz-devs.github.io|
53
- pymc-experimental.readthedocs.io|
53
+ pymc-extras.readthedocs.io|
54
54
  docs.pymc.io|
55
55
  numpy.org/doc|
56
56
  pymc-examples.readthedocs.io|
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytensor_ml
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: A(nother) deep learning library, built on top of Pytensor
5
- Project-URL: Homepage, https://github.com/jessegrabowski/pytensor_ml
6
- Project-URL: Source, https://github.com/jessegrabowski/pytensor_ml
7
- Project-URL: Tracker, https://github.com/jessegrabowski/pytensor_ml/issues
5
+ Project-URL: Homepage, https://github.com/pymc-devs/pytensor_ml
6
+ Project-URL: Source, https://github.com/pymc-devs/pytensor_ml
7
+ Project-URL: Tracker, https://github.com/pymc-devs/pytensor_ml/issues
8
8
  Author: Ricardo Vieira
9
9
  Author-email: Jesse Grabowski <jessegrabowski@gmail.com>
10
10
  License: Apache License
@@ -224,9 +224,11 @@ Classifier: Programming Language :: Python :: 3.14
224
224
  Classifier: Topic :: Scientific/Engineering
225
225
  Requires-Python: >=3.12
226
226
  Requires-Dist: numpy
227
- Requires-Dist: pymc<7.0.0,>=6.0.0
228
- Requires-Dist: pytensor<4.0.0,>=3.0.0
227
+ Requires-Dist: pytensor<4.0.0,>=3.2.3
228
+ Requires-Dist: safetensors
229
229
  Provides-Extra: dev
230
+ Requires-Dist: jax; extra == 'dev'
231
+ Requires-Dist: mypy; extra == 'dev'
230
232
  Requires-Dist: pre-commit; extra == 'dev'
231
233
  Requires-Dist: pytest; extra == 'dev'
232
234
  Requires-Dist: pytest-cov; extra == 'dev'
@@ -6,8 +6,7 @@ channels:
6
6
 
7
7
  dependencies:
8
8
  - python>=3.12
9
- - pytensor>=3.0.0,<4.0.0
10
- - pymc>=6.0.0,<7.0.0
9
+ - pytensor>=3.2.3,<4.0.0
11
10
  - numpy
12
11
  - scikit-learn
13
12
 
@@ -5,9 +5,9 @@ channels:
5
5
 
6
6
  dependencies:
7
7
  - python>=3.12
8
- - pytensor>=3.0.0,<4.0.0
9
- - pymc>=6.0.0,<7.0.0
8
+ - pytensor>=3.2.3,<4.0.0
10
9
  - numpy
10
+ - safetensors
11
11
  - scikit-learn
12
12
 
13
13
  # For testing
@@ -28,3 +28,9 @@ dependencies:
28
28
 
29
29
  # developer tools
30
30
  - pre-commit
31
+ - mypy
32
+
33
+ # JAX backend for dispatch tests; via pip so Windows pulls CPU wheels instead of conda jaxlib
34
+ - pip
35
+ - pip:
36
+ - jax
@@ -0,0 +1,198 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "15279e7f",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "%load_ext autoreload\n",
11
+ "%autoreload 2"
12
+ ]
13
+ },
14
+ {
15
+ "cell_type": "code",
16
+ "execution_count": null,
17
+ "id": "00915a61",
18
+ "metadata": {},
19
+ "outputs": [],
20
+ "source": [
21
+ "import pytensor\n",
22
+ "\n",
23
+ "floatX = \"float32\"\n",
24
+ "pytensor.config.floatX = floatX"
25
+ ]
26
+ },
27
+ {
28
+ "cell_type": "code",
29
+ "execution_count": null,
30
+ "id": "fae24c05",
31
+ "metadata": {},
32
+ "outputs": [],
33
+ "source": [
34
+ "import numpy as np\n",
35
+ "\n",
36
+ "from sklearn.datasets import load_digits\n",
37
+ "from sklearn.preprocessing import MinMaxScaler, OneHotEncoder\n",
38
+ "\n",
39
+ "from pytensor_ml.activations import LeakyReLU\n",
40
+ "from pytensor_ml.layers import BatchNorm2D, Dropout, Linear, Sequential\n",
41
+ "from pytensor_ml.loss import CrossEntropy\n",
42
+ "from pytensor_ml.model import Model\n",
43
+ "from pytensor_ml.optim import adagrad\n",
44
+ "from pytensor_ml.util import DataLoader"
45
+ ]
46
+ },
47
+ {
48
+ "cell_type": "code",
49
+ "execution_count": null,
50
+ "id": "65882b64",
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "X, y = load_digits(return_X_y=True)\n",
55
+ "y_onehot = OneHotEncoder().fit_transform(y[:, None]).toarray().astype(floatX)\n",
56
+ "X_normed = MinMaxScaler().fit_transform(X).astype(floatX)"
57
+ ]
58
+ },
59
+ {
60
+ "cell_type": "code",
61
+ "execution_count": null,
62
+ "id": "f81f9076",
63
+ "metadata": {},
64
+ "outputs": [],
65
+ "source": [
66
+ "# X_in = Input('X_in', shape=(64, ))\n",
67
+ "X_in = pytensor.tensor.tensor(\"X_in\", shape=(None, 64))"
68
+ ]
69
+ },
70
+ {
71
+ "cell_type": "code",
72
+ "execution_count": null,
73
+ "id": "41840fac",
74
+ "metadata": {},
75
+ "outputs": [],
76
+ "source": [
77
+ "prediction_network = Sequential(\n",
78
+ " Linear(\"Linear_1\", n_in=64, n_out=256),\n",
79
+ " BatchNorm2D(\"BatchNorm_1\"),\n",
80
+ " Dropout(\"Dropout_1\", p=0.5),\n",
81
+ " LeakyReLU(),\n",
82
+ " Linear(\"Linear_2\", n_in=256, n_out=128),\n",
83
+ " BatchNorm2D(\"BatchNorm_2\"),\n",
84
+ " Dropout(\"Dropout_2\", p=0.5),\n",
85
+ " LeakyReLU(),\n",
86
+ " Linear(\"Logits\", n_in=128, n_out=10),\n",
87
+ ")\n",
88
+ "\n",
89
+ "y_hat = prediction_network(X_in)\n",
90
+ "model = Model(X_in, y_hat, compile_kwargs={\"mode\": \"NUMBA\"})\n",
91
+ "model"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "code",
96
+ "execution_count": null,
97
+ "id": "3bfa0ddc",
98
+ "metadata": {},
99
+ "outputs": [],
100
+ "source": [
101
+ "loss_fn = CrossEntropy(expect_onehot_labels=True, expect_logits=True, reduction=\"mean\")"
102
+ ]
103
+ },
104
+ {
105
+ "cell_type": "code",
106
+ "execution_count": null,
107
+ "id": "290d3191",
108
+ "metadata": {},
109
+ "outputs": [],
110
+ "source": [
111
+ "# `model.compile_train` is the training companion to `model.predict`: it builds the loss against a target,\n",
112
+ "# differentiates it, folds in the BatchNorm running-statistic updates, and compiles a one-step training\n",
113
+ "# function (reusing the model's NUMBA compile settings).\n",
114
+ "step = model.compile_train(adagrad(learning_rate=1e-3), loss_fn, ndim_out=2)"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": null,
120
+ "id": "e77b697f",
121
+ "metadata": {},
122
+ "outputs": [],
123
+ "source": [
124
+ "model.initialize()"
125
+ ]
126
+ },
127
+ {
128
+ "cell_type": "code",
129
+ "execution_count": null,
130
+ "id": "dd6dede5",
131
+ "metadata": {},
132
+ "outputs": [],
133
+ "source": [
134
+ "from tqdm.notebook import tqdm\n",
135
+ "\n",
136
+ "loader = DataLoader(X_normed, y_onehot)\n",
137
+ "loss_history = []\n",
138
+ "n_epochs = 10_000\n",
139
+ "\n",
140
+ "for _ in tqdm(range(n_epochs)):\n",
141
+ " X_batch, y_batch = loader()\n",
142
+ " loss_value = step(X_batch, y_batch)\n",
143
+ " loss_history.append(loss_value)"
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": null,
149
+ "id": "ab1ad6ef",
150
+ "metadata": {},
151
+ "outputs": [],
152
+ "source": [
153
+ "import matplotlib.pyplot as plt\n",
154
+ "\n",
155
+ "plt.plot(loss_history)"
156
+ ]
157
+ },
158
+ {
159
+ "cell_type": "code",
160
+ "execution_count": null,
161
+ "id": "fb38dd99",
162
+ "metadata": {},
163
+ "outputs": [],
164
+ "source": [
165
+ "from scipy.special import softmax\n",
166
+ "\n",
167
+ "# `model.predict` compiles a forward pass specialized for inference: Dropout is dropped and BatchNorm uses its\n",
168
+ "# running statistics.\n",
169
+ "y_hat_logits = model.predict(X_normed)\n",
170
+ "y_hat_probs = softmax(y_hat_logits, axis=-1)\n",
171
+ "y_pred = np.argmax(y_hat_probs, axis=-1)"
172
+ ]
173
+ },
174
+ {
175
+ "cell_type": "code",
176
+ "execution_count": null,
177
+ "id": "4393b9c3",
178
+ "metadata": {},
179
+ "outputs": [],
180
+ "source": [
181
+ "import seaborn as sns\n",
182
+ "\n",
183
+ "from sklearn.metrics import confusion_matrix\n",
184
+ "\n",
185
+ "sns.heatmap(confusion_matrix(y, y_pred), annot=True, fmt=\"0.0f\")"
186
+ ]
187
+ }
188
+ ],
189
+ "metadata": {
190
+ "kernelspec": {
191
+ "display_name": "Python 3 (ipykernel)",
192
+ "language": "python",
193
+ "name": "python3"
194
+ }
195
+ },
196
+ "nbformat": 4,
197
+ "nbformat_minor": 5
198
+ }
@@ -34,22 +34,24 @@ keywords = [
34
34
  ]
35
35
 
36
36
  dependencies = [
37
- "pytensor>=3.0.0,<4.0.0",
37
+ "pytensor>=3.2.3,<4.0.0",
38
38
  "numpy",
39
- "pymc>=6.0.0,<7.0.0"
39
+ "safetensors"
40
40
  ]
41
41
 
42
42
  [project.urls]
43
- Homepage = "https://github.com/jessegrabowski/pytensor_ml"
44
- Source = "https://github.com/jessegrabowski/pytensor_ml"
45
- Tracker = "https://github.com/jessegrabowski/pytensor_ml/issues"
43
+ Homepage = "https://github.com/pymc-devs/pytensor_ml"
44
+ Source = "https://github.com/pymc-devs/pytensor_ml"
45
+ Tracker = "https://github.com/pymc-devs/pytensor_ml/issues"
46
46
 
47
47
  [project.optional-dependencies]
48
48
  dev = [
49
49
  "pre-commit",
50
50
  "pytest",
51
51
  "pytest-cov",
52
- "scikit-learn"
52
+ "scikit-learn",
53
+ "mypy",
54
+ "jax",
53
55
  ]
54
56
 
55
57
  [tool.hatch.version]
@@ -109,6 +111,24 @@ ignore = [
109
111
  ]
110
112
  fixable = ["ALL"]
111
113
 
114
+ [tool.ruff.lint.per-file-ignores]
115
+ # An __init__.py re-exports its package's API and imports submodules for their registration side effects.
116
+ "**/__init__.py" = ["F401"]
117
+
112
118
 
113
119
  [tool.ruff.lint.isort]
114
120
  lines-between-types = 1
121
+
122
+
123
+ [tool.mypy]
124
+ python_version = "3.12"
125
+ ignore_missing_imports = true
126
+ strict_equality = true
127
+ warn_redundant_casts = true
128
+ warn_unused_configs = true
129
+ warn_unused_ignores = true
130
+ # pytensor ops are typed to return Any, so returning their results from a function annotated with a
131
+ # concrete tensor type fires constantly without catching real bugs in our code.
132
+ warn_return_any = false
133
+ warn_unreachable = true
134
+ files = ["pytensor_ml"]
@@ -0,0 +1,29 @@
1
+ import logging
2
+ import sys
3
+
4
+ import pytensor_ml.dispatch # installs the lazy backend-dispatch registration hook
5
+
6
+ from pytensor_ml._version import __version__
7
+ from pytensor_ml.checkpoint import load_state, save_state
8
+ from pytensor_ml.pretrained import from_pretrained, load_network, save_network, save_pretrained
9
+ from pytensor_ml.pytensorf import function
10
+
11
+ _log = logging.getLogger(__name__)
12
+
13
+ if not logging.root.handlers:
14
+ _log.setLevel(logging.INFO)
15
+ if len(_log.handlers) == 0:
16
+ handler = logging.StreamHandler(sys.stderr)
17
+ _log.addHandler(handler)
18
+
19
+
20
+ __all__ = [
21
+ "__version__",
22
+ "from_pretrained",
23
+ "function",
24
+ "load_network",
25
+ "load_state",
26
+ "save_network",
27
+ "save_pretrained",
28
+ "save_state",
29
+ ]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.0.4'
22
- __version_tuple__ = version_tuple = (0, 0, 4)
21
+ __version__ = version = '0.0.6'
22
+ __version_tuple__ = version_tuple = (0, 0, 6)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -0,0 +1,225 @@
1
+ import numpy as np
2
+ import pytensor.tensor as pt
3
+
4
+ from pytensor import config
5
+
6
+ from pytensor_ml.base import Layer
7
+
8
+
9
+ def _constant_like(value: float, x: pt.TensorVariable) -> pt.TensorVariable:
10
+ """
11
+ Wrap a scalar so that combining it with ``x`` cannot widen ``x``'s dtype.
12
+
13
+ PyTensor's autocaster types a bare Python float by value, so whether a literal widens its operand
14
+ depends on that value: against a float32 input ``0.5 * x`` stays float32, while ``0.01 * x``
15
+ promotes to float64. Pinning the constant to ``x``'s dtype removes the dependence.
16
+ """
17
+ dtype = np.dtype(x.dtype)
18
+ # np.finfo maps complex64 -> float32, keeping complex inputs at their own precision.
19
+ dtype = np.finfo(dtype).dtype if np.issubdtype(dtype, np.inexact) else np.dtype(config.floatX)
20
+ return pt.constant(np.asarray(value, dtype=dtype))
21
+
22
+
23
+ class Activation(Layer): ...
24
+
25
+
26
+ class ReLU(Activation):
27
+ r"""
28
+ Rectified Linear Unit.
29
+
30
+ Compute the positive part of the input:
31
+
32
+ .. math::
33
+
34
+ \mathrm{ReLU}(x) = \max(0, x).
35
+ """
36
+
37
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
38
+ out = pt.maximum(0, x)
39
+ out.name = "ReLU"
40
+ return out
41
+
42
+
43
+ class LeakyReLU(Activation):
44
+ r"""
45
+ Leaky Rectified Linear Unit.
46
+
47
+ Replace ReLU's flat negative branch with a small negative slope, so negative inputs keep a
48
+ nonzero gradient:
49
+
50
+ .. math::
51
+
52
+ \mathrm{LeakyReLU}(x) = \begin{cases} x & x > 0 \\ \alpha x & x \le 0 \end{cases}
53
+
54
+ Parameters
55
+ ----------
56
+ negative_slope : float, optional
57
+ The slope :math:`\alpha` applied to negative inputs. Default is 0.01.
58
+ """
59
+
60
+ def __init__(self, negative_slope: float = 0.01):
61
+ self.negative_slope = negative_slope
62
+
63
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
64
+ x = pt.as_tensor(x)
65
+ out = pt.switch(x > 0, x, _constant_like(self.negative_slope, x) * x)
66
+ out.name = "LeakyReLU"
67
+ return out
68
+
69
+
70
+ class Tanh(Activation):
71
+ r"""
72
+ Hyperbolic tangent.
73
+
74
+ Squash the input to :math:`(-1, 1)`:
75
+
76
+ .. math::
77
+
78
+ \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}.
79
+ """
80
+
81
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
82
+ out = pt.tanh(x)
83
+ out.name = "TanH"
84
+ return out
85
+
86
+
87
+ class Sigmoid(Activation):
88
+ r"""
89
+ Logistic sigmoid.
90
+
91
+ Squash the input to :math:`(0, 1)`:
92
+
93
+ .. math::
94
+
95
+ \sigma(x) = \frac{1}{1 + e^{-x}}.
96
+ """
97
+
98
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
99
+ out = pt.sigmoid(x)
100
+ out.name = "Sigmoid"
101
+ return out
102
+
103
+
104
+ class SoftPlus(Activation):
105
+ r"""
106
+ Softplus activation.
107
+
108
+ Compute a smooth approximation to ReLU:
109
+
110
+ .. math::
111
+
112
+ \mathrm{softplus}(x) = \log(1 + e^x).
113
+ """
114
+
115
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
116
+ out = pt.softplus(x)
117
+ out.name = "SoftPlus"
118
+ return out
119
+
120
+
121
+ class GELU(Activation):
122
+ r"""
123
+ Gaussian Error Linear Unit.
124
+
125
+ Compute :math:`\mathrm{GELU}(x) = x \, \Phi(x)`, where :math:`\Phi` is the standard normal
126
+ cumulative distribution function:
127
+
128
+ .. math::
129
+
130
+ \mathrm{GELU}(x) = \frac{x}{2} \left(1 + \operatorname{erf}\!\left(\frac{x}{\sqrt{2}}\right)\right).
131
+
132
+ Parameters
133
+ ----------
134
+ approximate : bool, optional
135
+ Use the tanh approximation
136
+
137
+ .. math::
138
+
139
+ \mathrm{GELU}(x) \approx \frac{x}{2}
140
+ \left(1 + \tanh\!\left[\sqrt{2/\pi}\,(x + 0.044715\,x^3)\right]\right)
141
+
142
+ This is the variant HuggingFace calls ``"gelu_new"`` / ``"gelu_pytorch_tanh"``, PyTorch exposes
143
+ as ``nn.GELU(approximate="tanh")``, and Flax as ``gelu(approximate=True)``; GPT-2 uses it. It is
144
+ cheaper to evaluate than the exact :math:`\operatorname{erf}` form. Default is True.
145
+ """
146
+
147
+ def __init__(self, approximate: bool = True):
148
+ self.approximate = approximate
149
+
150
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
151
+ x = pt.as_tensor(x)
152
+ half = _constant_like(0.5, x)
153
+ one = _constant_like(1.0, x)
154
+ if self.approximate:
155
+ sqrt_2_over_pi = _constant_like(np.sqrt(2.0 / np.pi), x)
156
+ cubic_coef = _constant_like(0.044715, x)
157
+ out = half * x * (one + pt.tanh(sqrt_2_over_pi * (x + cubic_coef * x**3)))
158
+ else:
159
+ sqrt2 = _constant_like(np.sqrt(2.0), x)
160
+ out = half * x * (one + pt.erf(x / sqrt2))
161
+ out.name = "GELU"
162
+ return out
163
+
164
+
165
+ class Swish(Activation):
166
+ r"""
167
+ Swish activation, also known as SiLU.
168
+
169
+ Compute :math:`\mathrm{Swish}(x) = x \, \sigma(\beta x)`, where :math:`\sigma` is the logistic
170
+ sigmoid. With :math:`\beta = 1` this is the Sigmoid Linear Unit (PyTorch ``nn.SiLU``, HuggingFace
171
+ ``"silu"`` / ``"swish"``).
172
+
173
+ Parameters
174
+ ----------
175
+ beta : float, optional
176
+ Slope of the sigmoid gate. Larger :math:`\beta` sharpens the gate toward a ReLU; :math:`\beta
177
+ \to 0` collapses it toward the linear map :math:`x/2`. Default is 1.0.
178
+ """
179
+
180
+ def __init__(self, beta: float = 1.0):
181
+ self.beta = beta
182
+
183
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
184
+ x = pt.as_tensor(x)
185
+ out = x * pt.sigmoid(_constant_like(self.beta, x) * x)
186
+ out.name = "Swish"
187
+ return out
188
+
189
+
190
+ class Softmax(Activation):
191
+ r"""
192
+ Softmax activation.
193
+
194
+ Normalize the input along one axis into a probability distribution:
195
+
196
+ .. math::
197
+
198
+ \mathrm{softmax}(x)_i = \frac{e^{x_i}}{\sum_j e^{x_j}}.
199
+
200
+ Parameters
201
+ ----------
202
+ axis : int, optional
203
+ The axis along which the values sum to one. Default is -1.
204
+ """
205
+
206
+ def __init__(self, axis: int = -1):
207
+ self.axis = axis
208
+
209
+ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
210
+ out = pt.special.softmax(x, axis=self.axis)
211
+ out.name = "Softmax"
212
+ return out
213
+
214
+
215
+ __all__ = [
216
+ "GELU",
217
+ "Activation",
218
+ "LeakyReLU",
219
+ "ReLU",
220
+ "Sigmoid",
221
+ "SoftPlus",
222
+ "Softmax",
223
+ "Swish",
224
+ "Tanh",
225
+ ]