Skip to content

Promote modern pacemaker to master and document PyPI installation - #108

Merged
yury-lysogorskiy merged 35 commits into
masterfrom
release/promote-modern-pacemaker
Oct 31, 2026
Merged

yury-lysogorskiy merged 35 commits into
masterfrom
release/promote-modern-pacemaker

Conversation

@yury-lysogorskiy

@yury-lysogorskiy yury-lysogorskiy commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Promote the modern pacemaker branch into master, including the build/packaging improvements, bundled maxvol, GRACE/FS evaluator, current TensorPotential compatibility backend, and the merged Python/TensorFlow refresh from #106.

Update the installation guide and README to install the GRACE backend from PyPI. The distribution is named tensorpotential; grace-tensorpotential is the repository name and has no PyPI distribution. The released recipe uses tensorpotential==0.6.2.1, NumPy >=2,<2.4, TensorFlow ~=2.20.0 and matching legacy Keras, plus python-ace==0.4.0rc1. The source installation is retained for the merged TensorFlow 2.21/NumPy fixes, which are not yet in the backend's PyPI release. Source instructions now use master.

The application/build code is unchanged from the validated modern branch: all nine compatibility matrix jobs, both CLI workflows in each job, and wheel/source-distribution builds passed on #106. The updated guide passes mkdocs build --strict. The exact released-package pip installation passed in a fresh Python 3.12 environment: pip check passed, the TensorPotential fitting smoke test passed, and pacemaker --help succeeded. The promotion changes only README/install documentation relative to the already validated modern branch; the automatically repeated CI matrix is still running.

This updates the documentation source used by Read the Docs latest; publishing that site still depends on a successful Read the Docs build. No PyPI upload is part of this promotion.

Yury Lysogorskiy and others added 30 commits September 12, 2024 09:53
update to version 2024.7.4 with support for grace/fs
Update feature/grace_fs from master
…ce handling. it was breaking since python 3.13
refactor: Uniformly replace `.set_calculator` with `.calc` and update deprecated warnings
Brings in the 7 commits master gained since this branch forked at bb9c8ca:
top-level `cutoff` deprecation (now deduced from potential::bonds::rcut),
joint_df index reset, CI fixes (explicit maxvolpy install, UnitCellFilter
import), and docs/mkdocs cleanup.

Conflict resolution:
- tests/test_PyACECalculator.py: kept master's try/except around the
  UnitCellFilter import. This branch had a bare `from ase.filters import
  UnitCellFilter`, which fails on ASE < 3.23 (local env has 3.19.0).

bin/pacemaker and src/pyace/preparedata.py auto-merged in disjoint regions;
verified both sides survived (cutoff deprecation + importlib.resources;
reset_index + atoms.calc).
CMAKE_CXX_FLAGS_DEBUG was set to "-Ofast -DNDEBUG", making a Debug build
byte-for-byte equivalent to Release: no -g, no symbols, fully inlined. There
was no way to get a debuggable binary without editing this file.

Set it to "-Og -g" instead. Verified via generated flags.make that Release
(the default, forced when CMAKE_BUILD_TYPE is unset) still compiles with
"-Ofast -DNDEBUG -flto" and is unaffected; only DEBUG=1 / -DCMAKE_BUILD_TYPE=Debug
sees the new flags.

Also dropped the now-redundant commented-out '#set(CMAKE_CXX_FLAGS_DEBUG "-g")'
directly above, which contradicted the active setting.
Five translation units were listed in four different module targets each
(ace_radial, ace_spherical_cart, ace_abstract_basis, ace_clebsch_gordan,
ace_couplings), and CMake never shares object files between targets, so each
was compiled once per consuming module.

Group them into OBJECT libraries keyed on the *exact* set of modules that
already used each source, so every module links precisely the objects it
linked before. The grouping falls out cleanly into five libraries; no module
gains or loses an object.

The object libraries inherit the same compile settings pybind11_add_module
applies to the modules -- PIC, hidden visibility (needed: some of these are
pybind11 binding units) and the same LTO selection via pybind11::lto -- which
was verified against the generated build.ninja: the flags for a shared TU and
a module TU are identical.

Project object files: 59 -> 30.
Two coupled changes: reworking build_ext to drive one CMake tree is what makes
it possible to build maxvol as an ordinary extension alongside the CMake ones.

Build
-----
setuptools calls build_extension() once per Extension, and the old code gave
each its own build directory -- seven CMake configures and seven independent
builds per `pip install .`, recompiling yaml-cpp four times and cnpy three.
Override build_extensions() instead: one configure, one build of the default
target (which already covers all seven modules, so no CMake 3.15 multi-target
syntax is needed). Non-CMake extensions still go through setuptools.

Job count: os.cpu_count() reports host CPUs, so a container pinned to two
cores on a large host got -j19. Use the affinity mask, keep both cores on a
2-core box rather than reserving one, cap by available memory (cgroup v1/v2
aware), and stop overriding an explicit `build_ext -j`.

Use ccache/sccache as compiler launcher when present; PYACE_NO_COMPILER_CACHE
opts out. Add pyproject.toml declaring the build deps -- ninja was silently
absent, so the build had been falling back to make.

Measured in the tp2 env (20 cores, full test suite passing in all cases):

  baseline          126 s wall / 333 s CPU / 178 objects / 7 configures
  after              53 s wall / 181 s CPU /  60 objects / 1 configure
  after + ccache     32 s wall /  71 s CPU, peak RSS 780 MB -> 103 MB

  2 cores (taskset): 182 s -> ~156 s wall, 255 s -> 143 s CPU

maxvol
------
maxvolpy was installed by a custom `install` cmdclass that pip never runs when
it builds a wheel, so `pip install .` produced a pyace whose active-learning
entry points could not import; CI worked around it with an explicit
`pip install ./lib/maxvolpy`. It also compiled with -march=native, which bakes
the build machine's ISA into the binary, and -ffast-math, which is a poor fit
for a pivoting algorithm whose comparisons decide which rows get selected.

Vendor just the float64 square maxvol into src/pyace/maxvol (716 -> 127 lines
of Cython; the float32/complex variants and the rect_maxvol family are not
used by pyace) and build it as a normal cythonised extension. Keep a pure
numpy/scipy py_maxvol fallback so a missing or ABI-mismatched binary degrades
to slower rather than to ImportError.

Verified the compiled kernel and the fallback select identical rows with
max|C| agreeing to ~1e-15, on 10k x 200, 50k x 300 and 100k x 300; the same
figures reproduce the original maxvolpy results. Fallback exercised by
blocking the extension import.

Two fixes carried in the reduced kernel: the pivot-interchange loops used
prange, which is a data race on order-dependent swaps and was only safe
because nothing passed -fopenmp; and the scratch buffers now free in a
finally, so an error from dgetrf no longer leaks them.

numpy>=2.0 is now pinned at both build and run time, since the extension
compiles against the numpy C-API and the build-time numpy sets the runtime
floor. scipy becomes an explicit dependency (cython_blas/cython_lapack).

MIT notice for the vendored code ships in the wheel.
…itly

lib/maxvolpy is dead now that the float64 kernel is vendored in
src/pyace/maxvol; nothing outside the directory referenced it. 7.1 MB gone.

python_requires 3.8 -> 3.9, matching grace-tensorpotential's requires-python.
It was already wrong in practice: numpy>=2.0, which the maxvol extension
compiles against, needs 3.9.

default_parallel_jobs() already returned >= 1 on every path, but only
implicitly, spread across three branches. Clamp once at the return instead.
…ceiling

Metadata
--------
name, version, authors, description, readme, license, classifiers,
dependencies, package discovery, package data and scripts now live in
pyproject.toml's [project]/[tool.setuptools] tables, matching how
grace-tensorpotential declares itself. setup.py keeps only what setuptools has
no declarative form for -- ext_modules and the CMakeBuild cmdclass -- and
drops from 305 to ~250 lines.

The bin/ scripts stay script-files rather than becoming [project.scripts]
console entry points: they are standalone files with a shebang calling
main(sys.argv[1:]), so converting them (which would also fix Windows) needs
each one refactored into a module first.

versioneer
----------
Removed: versioneer.py, src/pyace/_version.py, setup.cfg, MANIFEST.in and
.version -- the last four existed only to serve it. Version is now stated once
in pyproject.toml, and pyace.__version__ reads it back via importlib.metadata,
falling back to "unknown" in an uninstalled source tree.

Version set to 0.4.0, taken from the .version file this branch had already
bumped. Note this is a visible change: versioneer derived it from git describe
and was reporting 0.2.7+N.g<sha>, while the newest tag (0.2.8) is not an
ancestor of this branch. 0.4.0 is what a source tarball would already have
reported, and it is written into potential metadata as "pacemaker_version".

numpy
-----
Dropped the "<2.2.0" ceiling. It came from d0f7a0b ("adjust numpy<2.2.0 with
tensorflow 2.19.1"), but pyace does not depend on tensorflow, and tensorflow
2.19.1 declares numpy<2.2.0 itself, so pip enforces it wherever it matters.
grace-tensorpotential allows tensorflow<=2.20, and tensorflow 2.20.0 requires
only numpy>=1.26.0 -- so keeping our own stale ceiling would block an
otherwise valid grace environment. Keep numpy>=2.0, which d0f7a0b had dropped
by replacing the whole line, and which a7e19fa deliberately introduced.

Correction to b64e60e's message: the build-time numpy does not set the runtime
floor. A binary built against the numpy 2.x C-API also runs under numpy 1.19+,
so building against >=2.0 is the widest-compatibility choice, and the runtime
floor is a decision about the Python code, not the ABI.

Verified in tp2: version 0.4.0 reported by both metadata and `pacemaker -v`,
all 7 pybind11 modules plus the maxvol kernel import, all 9 scripts installed,
package data present, `pip check` clean alongside tensorpotential 0.6.1 /
tensorflow 2.19.1, and 292 tests passing.
bin/ scripts -> src/pyace/cli/*.py with [project.scripts] entry points. This
retires setuptools' deprecated script-files, and makes the commands work on
Windows, where script-files are installed without an executable wrapper.

The scripts had three different shapes:
  - main() already: pace_activeset, pace_augment, pace_corerep, pace_select
    (valid entry points unchanged)
  - main(args): pacemaker, pace_collect -> main(args=None), since argparse
    already received `args` and falls back to sys.argv[1:] when it is None
  - no main at all, just module-level script body: pace_info, pace_timing,
    pace_yaml2yace -> body wrapped in main()

Shebangs dropped (they are modules now); the __main__ guards are kept, so
`python -m pyace.cli.pacemaker` still works alongside the installed command.

Nothing outside pyproject.toml referenced the bin/ paths -- the CLI
integration tests already invoked the commands by name, so they are unaffected.

requires-python gains an upper bound of <3.14. grace-tensorpotential, which
pyace must stay installable beside, declares only ">=3.9", so the real ceiling
comes from its tensorflow<=2.20 dependency: tensorflow 2.20 ships wheels for
3.9-3.13 and nothing newer. This needs revisiting when tensorflow adds 3.14.

Verified: 9 entry points registered, every command's --help runs, pacemaker -v
reports 0.4.0, `python -m pyace.cli.pace_info` still works,
tests/test-CLI/Cu-I/integration_test.sh passes end to end, and 292 tests pass.
requires-python is now ">=3.9", identical to grace-tensorpotential's.

The "<3.14" added in 17aba14 was derived from tensorflow<=2.20 shipping wheels
only up to 3.13, but an upper bound on requires-python is resolver metadata
that cannot be corrected after release: once tensorflow adds 3.14, every
already-published pyace would stay unselectable on it. The constraint is also
redundant -- pip cannot install tensorflow on a python it has no wheels for,
so a grace environment is limited whether or not pyace repeats the limit.
Four classes of warning, all from code added on this branch. Verified by
rebuilding with CMAKE_ARGS=-DCMAKE_VERBOSE_MAKEFILE=ON and diffing counts.

numpy C-API (2 -> 0)
  The vendored maxvol kernel compiled against the pre-1.7 numpy API, so every
  build printed numpy's "Using deprecated NumPy API" #warning. Define
  NPY_NO_DEPRECATED_API; it compiles clean, no source change needed.

setuptools / PEP 639 (9 -> 0)
  `project.license` as a TOML table and "License ::" classifiers are both
  deprecated, and setuptools warned six and three times respectively. Replace
  with an SPDX expression plus license-files, which also ships the vendored
  MIT notice as declared license metadata rather than only as package data.
  Needs setuptools>=77, hence the build-requires bump. Note the deprecated
  form was copied from grace-tensorpotential, which still has it.

uninitialised pivot indices (0 -> 0, exposed by the above)
  Defining NPY_NO_DEPRECATED_API changed Cython's codegen enough for gcc to
  notice that dmaxvol's i and j are only assigned inside the max-search loop.
  For a zero-column matrix that loop never runs and both were read
  uninitialised -- present in maxvolpy too. Initialise them and reject the
  degenerate shape up front.

build-time numpy cap
  numpy>=2.0 let pip pick any future major. The 2.x C-API is frozen so a
  wheel built against 2.2.6 imports fine under 2.1.3 (verified), but a numpy
  3.0 ABI break would silently raise the built wheel's runtime floor above the
  numpy>=2.0 we advertise. Cap the build pin at <3.

Left alone deliberately: four CMake cmake_minimum_required(3.7) deprecations
and CMP0148, which come from CMakeLists.txt plus vendored pybind11, and seven
gcc LTO "serial compilation of N LTRANS jobs" notices.
Brings in master's 15 commits since a743a1f: relaxed dependency constraints,
numpy >= 2.4 support, the ase UnitCellFilter fix and the pkg_resources removal.

Conflict resolution -- four files, all the same theme. Master added compat
shims for floors this branch has already raised, so HEAD's simpler form wins
and the shims are dropped as unreachable:

  setup.py            kept the slimmed version. Everything on master's side
                      (install_requires, classifiers, package_data, scripts,
                      python_requires) now lives in pyproject.toml, and
                      versioneer / InstallMaxVolPyLocalPackage no longer exist.
  radial.py           kept np.trapezoid. Master's
                      `np.trapezoid if hasattr(...) else np.trapz` shim covers
                      numpy 1.x, which numpy>=2.0 rules out; the now-unused
                      definition is removed.
  multispecies_       kept `importlib.resources.files`. Master's
    basisextension.py `_resource_filename` shim falls back to pkg_resources on
    cli/pacemaker.py  python 3.8, which requires-python ">=3.9" rules out --
                      and keeping it would reimport the very package master's
                      312922a set out to drop.

Master's dependency widening is real work and is ported into pyproject.toml
rather than discarded: numpy<3 (which also matches grace-tensorpotential),
pandas>=2,<4 and scikit-learn<2, replacing the bare pins. The numpy>=2.0 floor
is this branch's own, from a7e19fa.

Note git tracked bin/pacemaker -> src/pyace/cli/pacemaker.py across the rename,
so master's edit to it landed on the moved file. .github/workflows/test.yml
needed nothing: master's 63230d7 is not an ancestor here but its content was
already in the merge-base, so the maxvolpy CI step this branch removed was not
reintroduced.

Verified: clean build with none of the four warning classes returning, metadata
carries the merged pins, all 7 pybind11 modules plus the maxvol kernel import,
pacemaker -v reports 0.4.0, tests/test-CLI/Cu-I/integration_test.sh passes, and
292 tests pass.
The vendored copy was an unreleased dev snapshot (2.11.0.dev1) whose CMake
declared cmake_minimum_required(VERSION 3.7) and used FindPythonLibsNew,
producing a CMake deprecation warning plus a CMP0148 policy warning on CMake 4
-- both of which become hard errors in a future CMake. 2.13.6, the last 2.x,
still declares 3.5, so only the 3.x line actually fixes this: 3.1.0 declares
cmake_minimum_required(VERSION 3.15...4.2).

Checked before committing to it: pybind11 3.x does not require C++17. Its
common.h only hard-errors on Python < 3.8 and old compilers, and its CMakeLists
respects an externally set CMAKE_CXX_STANDARD, so CMAKE_CXX_STANDARD 11 still
works and no ACE source needed touching. 3.1.0's requires-python is >=3.9,
matching ours.

Our side needed two changes:
  - cmake_minimum_required 3.7 -> 3.15 in CMakeLists.txt, lib/ace/CMakeLists.txt
    and lib/ace/ace-evaluator/CMakeLists.txt. 3.15 is pybind11 3.x's floor, and
    this clears the remaining three deprecation warnings. Plain 3.15 rather
    than a range, to take 3.15 policy defaults rather than newer ones.
  - setup.py passes -DPython_EXECUTABLE alongside -DPYTHON_EXECUTABLE, since
    3.x uses FindPython (Python_EXECUTABLE) rather than FindPythonLibsNew.

Vendoring reduced to what the build consumes: CMakeLists.txt, include/, tools/,
pybind11/, LICENSE, README.rst, pyproject.toml. The old copy also carried
upstream's CI and lint config (.appveyor.yml, .clang-tidy, .cmake-format.yaml,
.readthedocs.yml, noxfield/setup.py/setup.cfg/MANIFEST.in); most no longer
exist in 3.1.0 and none had a role here. BSD-3-Clause LICENSE retained.

Verified: configure emits zero CMake warnings (was six), build has zero
compiler warnings, all 7 modules plus the maxvol kernel import, and 292 tests
pass.
The sdist has never been buildable. CMakeExtension declares sources=[], so
setuptools has no way to infer that lib/ace, lib/pybind11, CMakeLists.txt and
src/pyace/**/*.cpp are part of the build, and MANIFEST.in never listed them --
the only MANIFEST.in this repo ever had was the two-line versioneer one from
the initial release commit, removed in 0366094 along with the two files it
referenced. Measured before this change:

    pyace-0.4.0.tar.gz   88 entries, 0 .cpp, 0 .h, no lib/, no CMakeLists.txt

That matters for two reasons. `python -m build --sdist` followed by building
wheels from the sdist is the standard cibuildwheel flow, and it would fail at
cmake for every target. And the sdist is the fallback for any platform or
python we publish no wheel for: that path should be slow, not broken.

After:

    python-ace-0.4.0.tar.gz   402 entries, 68 .cpp, 148 .h, 10 CMakeLists.txt

lib/ace and lib/pybind11 are grafted wholesale rather than enumerated by
extension: those trees carry CMake fragments, LICENSE files, .clang-format and
yaml-cpp's own build files, and pattern lists are how a file goes missing
again later. _maxvol.pyx is listed explicitly because maxvol_extension() only
registers it as an Extension source when Cython is importable, which would
otherwise make the sdist's contents depend on the environment that built it.

A graft copies what is on disk rather than what git tracks, so a release sdist
must be built from a clean checkout. Speculative global-exclude guards against
in-tree build output were dropped: they matched nothing on a clean tree and
emitted nine "no previously-included files matching" warnings, which would
have undone 89b9ca4.
python-ace is the only name available to us on PyPI, so that is what the
distribution has to be called; the import package stays pyace. The split is
ordinary -- scikit-learn imports as sklearn, pillow as PIL -- and affects
nothing but the [project] name and one lookup.

That lookup is the subtle part. importlib.metadata.version() resolves a
*distribution* name, not an import package, so __init__ has to ask for
"python-ace" even though the surrounding module is pyace. Left as "pyace" it
raises PackageNotFoundError and silently reports __version__ == "unknown" for
every installed user, which is why the comment is there.

Verified from the built sdist:

    PKG-INFO       Name: python-ace   Version: 0.4.0
    top_level.txt  pyace

[project.scripts], packages.find and the pyace.data / pyace.maxvol
package-data keys are all import paths and are deliberately untouched.

Note python-ace 0.2.8 already exists on PyPI as a single hand-built cp39
wheel, and an unrelated pyace 0.1.1 sdist also exists; neither is produced by
this tree.
-Ofast implies -ffast-math, which lets gcc route exp/log/pow through glibc's
vector math library. Measured on a built evaluator module: 92 _ZGV* symbols
plus __exp_finite/__pow_finite, a libmvec.so.1 dependency, and a floor of
GLIBC_2.22.

auditwheel's manylinux_2_17 policy caps GLIBC at 2.17 and does not whitelist
libmvec (2_28 does), so with -Ofast the lowest tag a wheel can carry is
manylinux_2_28. Confirmed with a minimal translation unit:

    g++ -Ofast              libmvec: yes   3 x _ZGV*
    g++ -O3 -fno-fast-math  libmvec: no    0 x _ZGV*

The option is OFF by default, so building from source -- locally, on HPC --
keeps -Ofast and is unchanged. Only redistributable builds turn it on and
trade vectorised transcendental math for a glibc 2.17 floor, which adds
RHEL/CentOS 7 to the reachable set.

The cost of that trade has not been measured yet. The radial functions are
exactly the exp/pow-heavy code -ffast-math was helping, so wheels may be
measurably slower than a source build; compare with pace_timing before
assuming the trade is worth it. If it is not, reverting means dropping
PYACE_PORTABLE_MATH from the wheel job and restoring the manylinux_2_28 image.
Adds wheels.yml: cibuildwheel 4.2.0, linux x86_64, cp39-cp313, built in the
manylinux2014 image with PYACE_PORTABLE_MATH=ON so auditwheel can tag the
result manylinux_2_17.

Publishing is never automatic. A PyPI filename can be uploaded exactly once --
deleting or yanking does not free the version -- so a botched 0.4.0 would burn
0.4.0 permanently. Pushes to master or feature/** and pull requests build
cp312 only and upload artifacts; the full matrix and any upload require a
manual workflow_dispatch with an explicit publish target, defaulting to none.
There is deliberately no tag or release trigger. Uploads use trusted
publishing, so no API token is stored.

Note workflow_dispatch only appears in the Actions UI once this file is on the
default branch, which is a GitHub restriction. The push trigger is what makes
the build reachable from a feature branch before then; the publish job stays
gated on workflow_dispatch, so a push can never upload anything.

The image choice is constrained from both sides. Below: -Ofast's libmvec
dependency, hence the portable-math flag. Above: _maxvol.pyx cimports
scipy.linalg.cython_blas, so numpy and scipy must install *inside* the
container. Current releases are manylinux_2_28-only, but pip skips those as
incompatible and resolves back to the newest 2_17 build -- numpy 2.0.2-2.2.x
and scipy 1.13-1.16 all publish 2_17 wheels for cp39-cp313, as do cmake and
ninja, and Cython and setuptools are pure python. Runtime is unaffected.
manylinux_2_17 is also the floor: no manylinux_2_14 policy exists, and 2_12
(CentOS 6) has retired images and no modern numpy/scipy wheels.

CIBW_TEST_COMMAND checks four things against the installed wheel, each of
which has actually been broken here:

  1. __version__ != "unknown", i.e. the python-ace distribution lookup works
  2. all seven pybind11 modules import, covering the 2.11 -> 3.1 upgrade
  3. pyace.maxvol.c_maxvol exists and runs, so the compiled kernel shipped
     rather than silently falling back to py_maxvol
  4. a console entry point resolves, after bin/ became [project.scripts]

Both jobs also assert the sdist carries CMakeLists.txt, pybind11 and >=60 .cpp
files, so cb62309 cannot silently regress.

publish-to-test-pypi.yml: was on: push against a retired ubuntu-18.04 runner,
so it was inert -- but had the runner ever been updated it would have
published on every commit. Now workflow_dispatch only, with modernised
actions. It no longer builds a wheel: `build --wheel` on a bare runner emits a
linux_x86_64 tag, which PyPI and TestPyPI both reject, and that is not
fixable with a flag -- portable wheels need the container. skip-existing
avoids a duplicate-filename failure now that wheels.yml can publish the same
sdist.
The manylinux2014 build failed: pip downloaded numpy-2.5.2.tar.gz and died on
"NumPy requires GCC >= 10.3" inside the image.

a45bfb4 justified the 2_17 image by claiming pip would treat the newer
manylinux_2_28-only releases as incompatible and resolve back to the newest
2_17 build. That is wrong. pip prefers the newest *version* and falls back to
that version's sdist; it backtracks on dependency conflicts, not on wheel
availability. So numpy>=2.0,<3 selected 2.5.2 and tried to compile it.

Pinning build-time numpy/scipy to versions that do ship 2_17 wheels would not
rescue it, because cibuildwheel installs the runtime dependencies in the same
container to run CIBW_TEST_COMMAND, and pandas 3.0.5 and scikit-learn 1.9.0
publish x86_64 wheels only for manylinux_2_28. The same applies outside CI: a
glibc-2.17 user running `pip install python-ace` would get our wheel and then
a source build of pandas. There is no reach to be had below 2_28 while numpy,
scipy, pandas and scikit-learn all stop there -- 2_28 is the ecosystem floor,
covering RHEL/Alma/Rocky 8+, Ubuntu 20.04+ and Debian 10+.

PYACE_PORTABLE_MATH is consequently no longer passed to the wheel build:
manylinux_2_28 whitelists libmvec and permits GLIBC_2.22, so -Ofast is free
here and the wheels keep the vectorised transcendental math. The option
remains in CMakeLists.txt, unused by CI, documenting the -Ofast/libmvec/glibc
relationship for whenever a lower floor is worth chasing.
The first wheels.yml run warned that actions/checkout@v4 targets Node 20,
which is deprecated and being forced onto Node 24. The pins were further
behind than that warning suggested:

    actions/checkout           v4 -> v7
    actions/setup-python       v5 -> v7
    actions/upload-artifact    v4 -> v7
    actions/download-artifact  v4 -> v8

All four now run on node24, which clears the deprecation. Every input this
repo passes -- name, path, merge-multiple, python-version -- still exists in
the new majors, checked against each action.yml before bumping; upload v7 and
download v8 are both post-v4 immutable-artifact versions, so they interoperate.

pypa/cibuildwheel@v4.2.0 and pypa/gh-action-pypi-publish@release/v1 are
already current. test.yml still pins checkout@v3/@v2 and setup-python@v3 and
is left alone here -- it is a different workflow with its own tensorpotential
setup, worth bumping separately.
Everything verified so far is cp312 only. cp39 and cp313 are the two worth
actual doubt: cp39 resolves numpy to exactly 2.0.2, the only release in
>=2.0,<3 that publishes cp39 wheels, and cp313 is the reason pybind11 was
upgraded to 3.1 in the first place. A matrix entry that fails after a
successful upload cannot be fixed at the same version, so all five want
exercising before anything is published.

The full matrix was reachable only via workflow_dispatch, which GitHub does
not surface until the workflow file is on the default branch. Putting
[full-matrix] in a pushed commit's message now selects it too, so all five can
be built from a feature branch without merging anything to master first.
Routine commits still build one interpreter.

head_commit is absent on pull_request events, where contains() evaluates
false, so pull requests keep the single-interpreter build.

This commit's own message carries the marker, so the push that delivers the
change also runs the matrix it enables.
cb62309 taught MANIFEST.in about the C++ tree so the sdist could build. Those
sources live inside the package tree, and include-package-data defaults to
true when metadata comes from pyproject.toml, so they were also installed into
every wheel. Inspecting cp312 from the first full-matrix run:

    17 x .cpp/.h  (ace/, ace-evaluator/, utils/)
    pyace/maxvol/_maxvol.pyx and the generated _maxvol.c
    pyace/ace-evaluator/, which has no __init__.py and is not importable

Harmless but wrong: dead weight in every install, and a package directory that
exists only because a graft pattern matched it.

Setting include-package-data = false splits the two concerns properly --
MANIFEST.in governs the sdist, [tool.setuptools.package-data] governs what
ships installed. The three files that genuinely belong at runtime
(pyace/data/input_template.yaml, the rawlsLS pickle, and the vendored maxvol
MIT notice) are all already listed there, so nothing is lost. Verified the
sdist is unchanged: still 68 .cpp, 148 .h, 1 .pyx, 10 CMakeLists.txt.

PEP 639 license files are unaffected -- those come from license-files and land
in .dist-info/licenses/, not from package data.

Adds a wheel guard alongside the existing sdist one: every wheel must contain
zero .cpp/.h/.pyx/.c and exactly 8 .so (seven pybind11 modules plus _maxvol).
The .so count also catches a module silently failing to build, which is how
the maxvol extension used to disappear.
Yury Lysogorskiy and others added 5 commits September 4, 2026 18:26
A tutorial in the next few weeks needs a `pip install` that works reliably,
and TestPyPI cannot provide one: packaging.python.org states its database "may
be periodically pruned, so it is not unusual for user accounts to be deleted".
Installing from it also requires --extra-index-url, which makes pip take the
highest version of *every* dependency across both indexes -- the
dependency-confusion pattern, not something to teach. CI artifacts are no help
either; they expire after 90 days and need GitHub auth to fetch.

A pre-release on the real index solves it without spending 0.4.0. pip does not
select pre-releases unless asked, so `pip install python-ace` continues to
resolve to 0.2.8 for everyone else, while the tutorial pins
`python-ace==0.4.0rc1` and gets a permanent, CDN-backed wheel.

Bump back to a final version when the pre-release has been exercised.
Refresh Python and TensorFlow compatibility and documentation
@yury-lysogorskiy
yury-lysogorskiy merged commit db9e4f7 into master Oct 31, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant