OptiProfiler 2.0: See compatibility and release boundaries.
This repository provides an interface for OptiProfiler to access the PyCUTEst problem collection.
It contains adaptation tools allowing OptiProfiler to invoke and work with these problems.
- Adaptation Tools: Wrapper scripts and utilities in the root directory that bridge OptiProfiler with the PyCUTEst collection.
This adapter uses API-v1 and requires optiprofiler>=2.0.0. OptiProfiler
2.0.0 is not on PyPI until it is released, and this plugin is not published on
PyPI either. The 0.1.0 package value is build metadata, not a release
announcement.
The Python distribution name is optiprofiler-pycutest. It installs the
adapter package optiprofiler_pycutest and registers the problem-library entry
point
[project.entry-points."optiprofiler.problem_libraries"]
pycutest = "optiprofiler_pycutest:get_problem_library"The adapter package name intentionally avoids pycutest, which is reserved for
the upstream PyCUTEst runtime module. Installing or discovering this plugin does
not import PyCUTEst or compile CUTEst problems. Runtime availability is checked
only when OptiProfiler actually selects the pycutest problem library.
Install the adapter at the exact commit recorded in the core's
problem_libraries.lock, using normal dependency resolution:
python -m pip install "optiprofiler-pycutest @ git+https://github.com/optiprofiler/pycutest@<sha>"
python -m pip checkReplace <sha> with the full commit pinned in the core's
problem_libraries.lock: that is the commit tested with OptiProfiler 2.0.0. A
tag is created at the release. Until OptiProfiler 2.0.0 is on
PyPI, install that core first from its own wheel or checkout, because pip cannot
otherwise satisfy optiprofiler>=2.0.0. Do not work around this with
--no-deps: it skips the compatibility check that the requirement expresses.
pip keeps an installed adapter of the same version, so uninstall an earlier
source build before installing another commit.
This distribution intentionally installs only the OptiProfiler adapter. It does
not install or configure PyCUTEst, CUTEst, or MASTSIF. Users who want to run
PyCUTEst problems must prepare that runtime independently by following the
upstream PyCUTEst installation instructions.
OptiProfiler checks runtime availability only when the pycutest library is
selected; installing and discovering the adapter remain lightweight.
To remove only this adapter and its OptiProfiler entry point, run:
python -m pip uninstall optiprofiler-pycutestThis does not remove or reconfigure PyCUTEst, CUTEst, MASTSIF, compiled CUTEst problems, or user caches. Those external runtime components remain under their own installation and cleanup procedures.
Discovery reads the installed entry-point metadata without importing the upstream PyCUTEst runtime:
from optiprofiler import list_problem_libraries
assert "pycutest" in list_problem_libraries()Select the adapter with benchmark(..., plibs=["pycutest"]); an installed
plugin needs no filesystem path.
For a source checkout, update it and reinstall the adapter explicitly:
git pull --ff-only
python -m pip install -e .The OptiProfiler core repository records the tested adapter commit in
problem_libraries.lock. Do not assume that an arbitrary newer adapter commit
is compatible with an older core checkout.
Running python -m pip uninstall optiprofiler does not uninstall this adapter
or its upstream runtime. The adapter remains installed but cannot be used until
a compatible OptiProfiler core is installed again. Running
python -m pip uninstall optiprofiler-pycutest removes only this adapter and
its entry point; the independently managed runtime and caches are preserved as
described above.
On Linux/macOS the adapter serializes each complete upstream
pycutest.import_problem call and both adapter cache-clear operations with
a process lock in the effective cache root. This prevents workers from
creating the same cache directory or importing a module that is still being
built. An entry left incomplete by a killed build has no __init__.py, which
upstream writes last; the next load clears and rebuilds it under the lock.
The lock ends before problem conversion and solver/objective evaluation;
benchmark evaluations remain parallel. Warm imports use the same lock, so the
cache root must be writable.
The persistent .optiprofiler-import.lock file lives outside
pycutest_cache_holder and is not removed when clearing that holder. Its
presence does not mean a process holds the lock. Keep the cache environment
and working directory stable while loading, and use a filesystem that
supports POSIX advisory locks. The existing cache policy is unchanged:
PYCUTEST_CACHE takes precedence, otherwise the user cache is used. The adapter
puts that root first on sys.path and refuses a pycutest_cache_holder package
already imported from another cache, so set PYCUTEST_CACHE before importing
PyCUTEst. External callers bypassing this adapter do not participate in its lock.
Do not clear a cache while previously loaded native problems are in use.
Permission, path, and native build errors still propagate; the adapter does
not retry, repair partial entries, or modify upstream PyCUTEst source.
Recent gfortran releases vectorize math calls at -O2 through glibc's libmvec,
and upstream PyCUTEst does not link that library. On such a toolchain (observed
with gfortran 15 and glibc 2.43), about 2% of the problems then fail to import
with an undefined symbol such as _ZGVbN2vv_pow, and OptiProfiler reports them
as load failures. Preloading the library fixes them without a rebuild:
LD_PRELOAD=libmvec.so.1 python your_benchmark.pyThe adapter does not preload libraries or change upstream build flags for you.
The file config.txt in this directory controls how pycutest_select filters problems (e.g., variable_size and test_feasibility_problems). See the comments in config.txt for a full description of each option.
For a reproducible OptiProfiler experiment, pass the options explicitly for this run:
from optiprofiler import benchmark
benchmark(
solvers,
plibs=['pycutest'],
plib_options={
'pycutest': {
'variable_size': 'all',
'test_feasibility_problems': 2,
},
},
)OptiProfiler stores the validated effective mapping with the experiment. For a process-level default shared by subsequent calls, the compatibility API remains available:
from optiprofiler import set_plib_config, get_plib_config
# View the current effective configuration
print(get_plib_config('pycutest'))
# Override subsequent calls in the current Python process
set_plib_config('pycutest', variable_size='all', test_feasibility_problems=2)The precedence is per-run plib_options, process-level set_plib_config,
environment variables, config.txt, then built-in defaults. You can also set
PYCUTEST_VARIABLE_SIZE and PYCUTEST_TEST_FEASIBILITY_PROBLEMS directly.
The adapter merges these layers first and validates the final mapping once, so
an explicit valid per-run value can replace an invalid lower-priority value.
Inspecting these options does not import PyCUTEst or require a working CUTEst
installation. Runtime availability is checked only when this library is
selected for a benchmark.
PyCUTEst and MASTSIF are external runtimes, and an upstream SIF file can change
which parameter values are active by default. The adapter therefore records
the reviewed default SIF parameters for every variable-size problem in
sif_defaults_pycutest.json. Loading a bare variable-size name applies those
parameters explicitly; loading an encoded name such as
OSCIPATH_N_25_RHO_1 applies the parameters encoded in that identity.
After loading a reviewed instance, the adapter checks its dimension and its
bound, linear-constraint, nonlinear-constraint, and total-constraint counts
against probinfo_pycutest.csv. A mismatch fails with a runtime/metadata-drift
error instead of benchmarking a different instance from the one selected.
Explicit custom SIF parameter mappings remain supported, but configurations
not present in the reviewed metadata do not claim a recorded shape.
Loading fails closed when native constraint bounds have invalid shapes or NaN entries, or when required linear constraint values/Jacobians cannot be read. The adapter does not replace failed linear constraints with empty matrices. Purely nonlinear problems do not need a Jacobian probe during loading. The regression suite includes injected failures through the actual loader, real CUTEst U/B/L/N controls, and linear offset/sign checks.
The CI workflow runs daily and on pushes on Linux. It installs CUTEst/PyCUTEst, checks the OptiProfiler adapter layer, and keeps the sample intentionally small:
- load
ROSENBRthroughpycutest_loadand evaluatefun,cub, andceq; - select small problems through
pycutest_select; - check
variable_sizeandtest_feasibility_problemsenvironment overrides; - audit all 341 variable-size SIF parameter definitions and all 2,588 recorded nondefault configurations against the committed metadata;
- verify that
OSCIPATHselected in the dimension range 8--10 loads the reviewed 10-dimensional instance by passing its SIF parameters explicitly; - check the installed entry-point factory without importing PyCUTEst;
- sample a few additional small problems each day with at most two numerical-library threads.
Runtime-dependent tests need an installed PyCUTEst/CUTEst environment; mocked adapter conversion tests do not. From this repository:
python -m unittest discover -s tests -p 'test_*.py'The manual Collect Problem Info workflow regenerates the metadata used by
pycutest_select. It is heavier than the smoke CI because it installs the
stable compatibility baseline from
.github/actions/collect_info/runtime-versions.env and scans the problem list
in blocks. Its artifact includes both probinfo_pycutest.csv and the reviewed
sif_defaults_pycutest.json; it does not update the adapter automatically.
Maintainers can verify the committed defaults against an installed copy of the
pinned runtime with:
python scripts/collect_sif_defaults.py --checkCheck Runtime Upstream compares that baseline with the latest PyCUTEst,
CUTEst, SIFDecode, ARCHDefs, and MASTSIF tags. A newer tag creates or updates an
upstream-update issue, but the adapter neither installs it for users nor
changes the CI baseline without maintainer review.
The currently reviewed runtime matrix is:
| Component | Version | Commit |
|---|---|---|
| PyCUTEst | 1.8.2 | 65fb333f6886386cd38eb0ecdfaff9f536e931de |
| ARCHDefs | v2.2.3 | d1bf9f0225a97981aac1b6fddb85c07d8e9676a4 |
| SIFDecode | v2.1.3 | c0e654b76d925cb59accc03c75f65e6ac6654e32 |
| CUTEst | v2.0.42 | 00999c3e215a4dffd57f5a888fc4c4ada2567940 |
| MASTSIF | v0.5 | 04280876fc4c7cae9a06ab34e0d5887bee4d8960 |
The same matrix is machine-readable in
.github/actions/collect_info/runtime-versions.env and
sif_defaults_pycutest.json. Upstream notifications are review inputs, not
automatic deployment instructions.
Before promoting a newer runtime matrix, maintainers must also run the adapter
regression suite against that candidate runtime. This catches upstream changes
such as a different bare SIF default without changing the reviewed instance
used by pycutest_select and pycutest_load.
The OptiProfiler adapter code and generated metadata in this repository are licensed under the BSD 3-Clause License. The built distribution does not include PyCUTEst, CUTEst, or MASTSIF source code or compiled libraries.
PyCUTEst is an optional external runtime dependency licensed under GPL-3.0-or-later. CUTEst and MASTSIF are installed separately and retain their own upstream provenance, citation, and license requirements. See THIRD_PARTY_NOTICES.md for the component boundaries and upstream links.