Skip to content

Commit 5b04d01

Browse files
committed
clean up and rerun some old code, update dependencies, update python versions
1 parent b2aac22 commit 5b04d01

12 files changed

Lines changed: 66 additions & 96 deletions

File tree

.github/workflows/jax_tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ jobs:
8282
0.7.0,
8383
0.7.1,
8484
0.7.2,
85+
0.8.0,
86+
0.9.1,
8587
]
8688
group: [1, 2]
8789
steps:
@@ -99,7 +101,6 @@ jobs:
99101
sed -i '1i\jax[cpu] == ${{ matrix.jax-version }}' ./requirements.txt
100102
cat ./requirements.txt
101103
pip install -r ./devtools/dev-requirements.txt
102-
pip install matplotlib==3.9.2
103104
- name: Verify dependencies
104105
run: |
105106
python --version

.github/workflows/main_tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
- "3.11"
2020
- "3.12"
2121
- "3.13"
22+
- "3.14"
2223

2324
steps:
2425
- uses: actions/checkout@v4

.github/workflows/notebook_tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
python-version:
18-
- "3.10"
18+
- "3.13"
1919

2020
steps:
2121
- uses: actions/checkout@v4
@@ -29,7 +29,6 @@ jobs:
2929
python -m pip install --upgrade pip
3030
pip install -r dev-requirements.txt
3131
32-
3332
- name: Test notebooks with pytest and nbmake
3433
run: |
3534
pwd

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
- name: Set up Python
1313
uses: actions/setup-python@v5
1414
with:
15-
python-version: "3.10"
15+
python-version: "3.12"
1616
- name: Install dependencies
1717
run: |
1818
python -m pip install --upgrade pip

README.md

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
# Zernipax
2-
A python library to calculate Zernike Polynomials fast and accurately using JAX.
2+
A python library to calculate Zernike Polynomials fast and accurately using JAX. Available on `PyPI`,
33

4-
To create conda environment:
54
```
6-
conda create --name zernipax-env 'python>=3.8, <=3.12'
7-
conda activate zernipax-env
8-
pip install -r requirements.txt
9-
```
10-
11-
or, if you want to contribute
12-
```
13-
pip install -r dev-requirement.txt
5+
pip install zernipax
146
```
157

168
## GPU Support
@@ -24,16 +16,14 @@ pip install jax[cuda12]
2416
If you use this repository in your projects, please cite it as:
2517
```
2618
@article{ELMACIOGLU2025129534,
27-
title = {ZERNIPAX: A fast and accurate Zernike polynomial calculator in Python},
28-
journal = {Applied Mathematics and Computation},
29-
volume = {505},
30-
pages = {129534},
31-
year = {2025},
32-
issn = {0096-3003},
33-
doi = {https://doi.org/10.1016/j.amc.2025.129534},
34-
url = {https://www.sciencedirect.com/science/article/pii/S0096300325002607},
35-
author = {Yigit Gunsur Elmacioglu and Rory Conlin and Daniel W. Dudt and Dario Panici and Egemen Kolemen},
36-
keywords = {Zernike polynomials, Optics, Astrophysics, Spectral simulations, Python, JAX, CPU/GPU computing},
37-
abstract = {Zernike polynomials serve as an orthogonal basis on the unit disc, and have proven to be effective in optics simulations, astrophysics, and more recently in plasma simulations. Unlike Bessel functions, Zernike polynomials are inherently finite and smooth at the disc center (r=0), ensuring continuous differentiability along the axis. This property makes them particularly suitable for simulations, requiring no additional handling at the origin. We developed ZERNIPAX, an open-source Python package capable of utilizing CPU/GPUs, leveraging Google's JAX package and available on GitHub as well as the Python software repository PyPI. Our implementation of the recursion relation between Jacobi polynomials significantly improves computation time compared to alternative methods by use of parallel computing while still performing more accurately for high-mode numbers.}
19+
title = {ZERNIPAX: A fast and accurate Zernike polynomial calculator in Python},
20+
journal = {Applied Mathematics and Computation},
21+
volume = {505},
22+
pages = {129534},
23+
year = {2025},
24+
issn = {0096-3003},
25+
doi = {https://doi.org/10.1016/j.amc.2025.129534},
26+
url = {https://www.sciencedirect.com/science/article/pii/S0096300325002607},
27+
author = {Yigit Gunsur Elmacioglu, Rory Conlin, Daniel W. Dudt, Dario Panici and Egemen Kolemen},
3828
}
3929
```

dev-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pre-commit
1111

1212
# testing and benchmarking
1313
nbmake <= 1.5.5
14-
pytest ~= 8.0.0
14+
pytest ~= 9.0
1515
pytest-benchmark
1616
pytest-cov >= 2.6.0
1717
pytest-monitor

notebooks/benchmarks.ipynb

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,11 @@
4444
},
4545
{
4646
"cell_type": "code",
47-
"execution_count": 3,
47+
"execution_count": null,
4848
"metadata": {},
4949
"outputs": [],
5050
"source": [
5151
"res = 12\n",
52-
"# basis = ZernikePolynomial(L=res, M=res, spectral_indexing=\"ansi\", sym=\"cos\")\n",
5352
"basis = FourierZernikeBasis(L=res, M=res, N=res)\n",
5453
"r = np.linspace(0, 1, 1000)"
5554
]
@@ -63,15 +62,15 @@
6362
"name": "stderr",
6463
"output_type": "stream",
6564
"text": [
66-
"WARNING:2025-10-19 18:04:12,507:jax._src.xla_bridge:794: An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n"
65+
"WARNING:2026-02-24 16:12:18,619:jax._src.xla_bridge:794: An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n"
6766
]
6867
},
6968
{
7069
"name": "stdout",
7170
"output_type": "stream",
7271
"text": [
73-
"9.89 ms ± 827 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
74-
"46.7 ms ± 582 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
72+
"10.9 ms ± 367 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
73+
"46.4 ms ± 664 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
7574
]
7675
}
7776
],
@@ -96,13 +95,13 @@
9695
"output_type": "stream",
9796
"text": [
9897
"zernike_radial_poly, 0th derivative\n",
99-
"5.61 ms ± 318 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
98+
"4.81 ms ± 273 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
10099
"zernike_radial_poly, 1st derivative\n",
101-
"5.82 ms ± 79.8 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
100+
"4.81 ms ± 133 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
102101
"zernike_radial_poly, 2nd derivative\n",
103-
"5.83 ms ± 148 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
102+
"4.6 ms ± 273 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
104103
"zernike_radial_poly, 3rd derivative\n",
105-
"6.46 ms ± 467 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
104+
"4.75 ms ± 115 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
106105
]
107106
}
108107
],
@@ -126,40 +125,16 @@
126125
"# Exact computation\n",
127126
"mpmath.mp.dps = 100\n",
128127
"c = zernike_radial_coeffs(basis.modes[:, 0], basis.modes[:, 1], exact=True)\n",
129-
"zt0 = np.array([np.asarray(mpmath.polyval(list(ci), r), dtype=float) for ci in c]).T\n",
130-
"zt1 = np.array([np.asarray(mpmath.polyval(list(ci), r), dtype=float) for ci in polyder_vec(c, 1, exact=True)]).T\n",
131-
"zt2 = np.array([np.asarray(mpmath.polyval(list(ci), r), dtype=float) for ci in polyder_vec(c, 2, exact=True)]).T\n",
132128
"zt3 = np.array([np.asarray(mpmath.polyval(list(ci), r), dtype=float) for ci in polyder_vec(c, 3, exact=True)]).T\n",
133129
"# CPU version\n",
134-
"zr0 = zernike_radial_cpu(r, basis.modes[:, 0], basis.modes[:, 1], 0)\n",
135-
"zr1 = zernike_radial_cpu(r, basis.modes[:, 0], basis.modes[:, 1], 1)\n",
136-
"zr2 = zernike_radial_cpu(r, basis.modes[:, 0], basis.modes[:, 1], 2)\n",
137130
"zr3 = zernike_radial_cpu(r, basis.modes[:, 0], basis.modes[:, 1], 3)\n",
138131
"# GPU version\n",
139-
"zd0 = zernike_radial_gpu(\n",
140-
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], 0\n",
141-
")\n",
142-
"zd1 = zernike_radial_gpu(\n",
143-
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], 1\n",
144-
")\n",
145-
"zd2 = zernike_radial_gpu(\n",
146-
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], 2\n",
147-
")\n",
148132
"zd3 = zernike_radial_gpu(\n",
149133
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], 3\n",
150134
")\n",
151135
"\n",
152136
"mpmath.mp.dps = 10\n",
153137
"# Polynomial computation\n",
154-
"zp0 = zernike_radial_poly(\n",
155-
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], dr=0, exact=False\n",
156-
")\n",
157-
"zp1 = zernike_radial_poly(\n",
158-
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], dr=1, exact=False\n",
159-
")\n",
160-
"zp2 = zernike_radial_poly(\n",
161-
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], dr=2, exact=False\n",
162-
")\n",
163138
"zp3 = zernike_radial_poly(\n",
164139
" r[:, np.newaxis], basis.modes[:, 0], basis.modes[:, 1], dr=3, exact=False\n",
165140
")"
@@ -187,7 +162,7 @@
187162
},
188163
{
189164
"cell_type": "code",
190-
"execution_count": 8,
165+
"execution_count": 10,
191166
"metadata": {},
192167
"outputs": [
193168
{
@@ -196,11 +171,11 @@
196171
"text": [
197172
"zernike_radial, derivative order: 0\n",
198173
"# With no duplicate modes (might have lacking modes)\n",
199-
"929 μs ± 327 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n",
200-
"# With all the checks necessary but no reverse mode AutoDiff capable\n",
201-
"11.3 ms ± 480 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
174+
"1.24 ms ± 308 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n",
175+
"# With all the checks necessary\n",
176+
"12.9 ms ± 274 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
202177
"# With all the checks necessary but less efficient on CPU\n",
203-
"41.1 ms ± 1.19 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
178+
"44.6 ms ± 418 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
204179
]
205180
}
206181
],
@@ -211,7 +186,7 @@
211186
"print(\"# With no duplicate modes (might have lacking modes)\")\n",
212187
"%timeit _ = zernike_radial_unique(r, basis.modes[:,0], basis.modes[:,1], dr).block_until_ready()\n",
213188
"\n",
214-
"print(\"# With all the checks necessary but no reverse mode AutoDiff capable\")\n",
189+
"print(\"# With all the checks necessary\")\n",
215190
"%timeit _ = zernike_radial_cpu(r, basis.modes[:,0], basis.modes[:,1], dr).block_until_ready()\n",
216191
"\n",
217192
"print(\"# With all the checks necessary but less efficient on CPU\")\n",
@@ -220,7 +195,7 @@
220195
},
221196
{
222197
"cell_type": "code",
223-
"execution_count": 9,
198+
"execution_count": 11,
224199
"metadata": {},
225200
"outputs": [
226201
{
@@ -229,11 +204,11 @@
229204
"text": [
230205
"zernike_radial, derivative order: 1\n",
231206
"# With no duplicate modes (might have lacking modes)\n",
232-
"1.42 ms ± 303 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n",
233-
"# With all the checks necessary but no reverse mode AutoDiff capable\n",
234-
"11.1 ms ± 303 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
207+
"1.93 ms ± 6.67 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n",
208+
"# With all the checks necessary\n",
209+
"13.2 ms ± 303 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
235210
"# With all the checks necessary but less efficient on CPU\n",
236-
"68.9 ms ± 460 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
211+
"75.4 ms ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
237212
]
238213
}
239214
],
@@ -244,7 +219,7 @@
244219
"print(\"# With no duplicate modes (might have lacking modes)\")\n",
245220
"%timeit _ = zernike_radial_unique(r, basis.modes[:,0], basis.modes[:,1], dr).block_until_ready()\n",
246221
"\n",
247-
"print(\"# With all the checks necessary but no reverse mode AutoDiff capable\")\n",
222+
"print(\"# With all the checks necessary\")\n",
248223
"%timeit _ = zernike_radial_cpu(r, basis.modes[:,0], basis.modes[:,1], dr).block_until_ready()\n",
249224
"\n",
250225
"print(\"# With all the checks necessary but less efficient on CPU\")\n",

notebooks/tutorial-basis.ipynb

Lines changed: 11 additions & 11 deletions
Large diffs are not rendered by default.

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
jax[cpu] >= 0.3.2, <= 0.6.2
2-
matplotlib >= 3.5.0, <= 4.0.0
1+
jax[cpu] >= 0.3.2
2+
matplotlib >= 3.5.0
33
mpmath >= 1.0.0
4-
numpy >= 1.20.0, < 2.0.0
4+
numpy >= 1.20.0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
keywords="zernike polynomials, optics, astrophysics, spectral "
3434
+ "simulation, basis, orthogonal polynomials, parallel computing, "
3535
+ "JAX",
36-
packages=find_packages(exclude=["tests", "notebooks", "figures"]),
36+
packages=find_packages(include=["zernipax"]),
3737
include_package_data=True,
3838
install_requires=requirements,
3939
python_requires=">=3.10",

0 commit comments

Comments
 (0)