Repository navigation
Add LinearSolver enum, remove boolean solver-selection flags - #189
Merged
Merged
Conversation
Introduces a `linear_solver=scs.LinearSolver.<SOLVER>` parameter as the single way to select the linear system solver backend. The default is `AUTO`, which picks the best available solver for the platform: - macOS: Apple Accelerate if available, otherwise QDLDL - Linux/Windows: MKL Pardiso if available, otherwise QDLDL The old boolean flags (gpu, mkl, apple_ldl, dense, cudss, use_indirect) still work but now emit a DeprecationWarning. Combining old flags with the new `linear_solver` parameter raises a ValueError. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the boolean flags (gpu, mkl, apple_ldl, dense, cudss, use_indirect) with a single `linear_solver=scs.LinearSolver.<SOLVER>` parameter using dict dispatch. Available solvers: AUTO, QDLDL, INDIRECT, MKL, ACCELERATE, DENSE, GPU, CUDSS. The default is AUTO, which picks the best available solver for the platform: - macOS: Apple Accelerate if available, otherwise QDLDL - Linux/Windows: MKL Pardiso if available, otherwise QDLDL Also accepts string values, e.g. linear_solver="mkl". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add LinearSolver.AUTO to all parametrized solver tests (tolerances, QP, exp cone, SDP) - Add explicit tests for AUTO via enum and string - Add mock-based unit tests for _resolve_auto: verifies macOS tries Accelerate, Linux/Windows try MKL, and all platforms fall back to QDLDL when the preferred module is missing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- README: document linear_solver parameter, AUTO defaults, and available enum values - scs_source docs: replace old boolean flags with LinearSolver enum, add table of all solver values with descriptions and examples, describe AUTO behavior for each platform Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The docs changes are in a separate PR (cvxgrp/scs#381). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bodono
added a commit
that referenced
this pull request
Apr 17, 2026
Missed during the rebase onto master: this test still passed the old `use_indirect=` boolean kwarg, which was removed in #189 in favor of the `linear_solver=scs.LinearSolver.*` enum. This broke the full CI matrix (wheel builds, accelerate builds, free-threading tests, sanitizers) on a single shared failure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
bodono
added a commit
that referenced
this pull request
Apr 17, 2026
* Add free-threading (no-GIL) support for Python 3.13t+ Enables scs to run with the GIL disabled in free-threaded CPython builds. C extension changes: - Add PyMutex per-instance lock (only in Py_GIL_DISABLED builds) to serialize concurrent access to the SCS workspace from multiple threads - Lock around scs_solve, scs_update, and scs_finish (pure C calls safe to hold across Py_BEGIN_ALLOW_THREADS) - No locking in SCS_init (object is thread-local during construction) - Declare module as GIL-not-used via PyUnstable_Module_SetGIL Build/packaging: - Enable cpython-freethreading in cibuildwheel to ship 3.13t+ wheels - Add free-threading PyPI classifier CI: - Add free-threading test workflow (3.13t + 3.14t) alongside existing build.yml Tests: - 15 new concurrency tests covering: independent instances, shared instances, solve+update sequences, warm starts, legacy API, stress tests, result isolation Closes #130 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix free-threading CI: create venv before installing packages uv pip install requires a virtual environment or --system flag. Use uv venv to create a proper venv for the free-threaded Python. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix free-threading CI: install build deps for --no-build-isolation meson-python and meson must be in the venv when using --no-build-isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix free-threading CI: use build isolation instead of --no-build-isolation Let the build system handle its own dependencies (meson-python, numpy) via pyproject.toml build-system.requires, rather than manually installing them and using --no-build-isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix free-threading CI: install pytest-timeout for --timeout flag Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address free-threading review feedback and make all tests thread-safe Correctness fixes from @ngoldbaum's review: - PyDict_GetItemString -> PyDict_GetItemStringRef (strong refs) - PyList_GetItem -> PyList_GetItemRef (strong refs) - Proper Py_DECREF and scs_free on all error paths - Move self->work NULL check under lock (TOCTOU fix) in SCS_solve/SCS_update - Check PyThread_acquire_lock return value (PY_LOCK_ACQUIRED) - Add comment to SCS_finish explaining unchecked lock acquire in dealloc path - Fix SCS_update lock release ordering with comment explaining why it differs from SCS_solve (avoid holding instance lock while waiting for GIL) Thread-safe RNG in all test files: - Replace all np.random.seed() + global RNG with local RandomState instances - Each test file gets unique seeds so tests exercise different random data - Backend-variant tests (direct/dense/mkl/cudss) share seeds intentionally since they test the same problem with different linear system solvers Testing infrastructure: - 27 new threading tests covering borrowed refs, TOCTOU races, concurrent solve+update, re-init races, error path lock release, stress tests - GIL re-enable detection in conftest.py (modeled after NumPy) - pytest-run-parallel in CI (--parallel-threads=4 --iterations=3) - TSan CI job with cached CPython 3.14t build - faulthandler_timeout and thread_unsafe markers in pyproject.toml - tsan-suppressions.txt for known CPython-internal races Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix thread-safe signal handling and link pthreads Update scs_source submodule to thread-safe-ctrlc branch which adds mutex-protected reference counting for signal handler registration, fixing TSan-detected data races when multiple threads solve concurrently. Add pthreads dependency to meson.build for the ctrlc mutex. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Set OPENBLAS_NUM_THREADS=1 for TSan CI OpenBLAS spawns internal threads that are not instrumented with TSan annotations, causing false positive data race reports (e.g. in dsyrk worker threads racing with the caller after the BLAS call returns). Forcing single-threaded BLAS under TSan is standard practice. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add ASan CI job via sanitizer matrix Refactor the TSan CI job into a matrix over {tsan, asan}. Both build CPython 3.14t from source with the corresponding sanitizer flag. Runtime options (TSAN_OPTIONS, ASAN_OPTIONS, OPENBLAS_NUM_THREADS) are set via GITHUB_ENV in a shared setup step. ASan uses detect_leaks=0 to avoid false positives from CPython's internal memory allocator. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Set sanitizer env vars before build step ASan's LeakSanitizer was killing the build because ASAN_OPTIONS was only set after the build. Move the env setup step before pip install so detect_leaks=0 applies during compilation too. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Move sanitizer suppressions to test/, add LSan suppressions Move tsan-suppressions.txt to test/ and add lsan-suppressions.txt for CPython-internal leaks. Use LSan suppressions instead of detect_leaks=0 so that real SCS leaks are still caught. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Stabilize Accelerate backend tests * Keep Accelerate random-problem tests deterministic * Add faulthandler_exit_on_timeout and simplify CI pytest flags Set faulthandler_exit_on_timeout = true in pyproject.toml so hanging tests actually terminate in CI instead of just dumping tracebacks. Remove redundant -p no:faulthandler -o faulthandler_timeout=600 flags from the free-threading CI since the config is now in pyproject.toml. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address review: update FT classifier, drop cibuildwheel enable, cleanup - Fix Free Threading trove classifier: drop extraneous "Implementation ::" prefix, bump "1 - Unstable" to "3 - Stable" per PEP 779 and reviewer feedback (free-threading is no longer experimental in 3.14). - Drop `enable = ["cpython-freethreading"]`: deprecated in cibuildwheel 3.4.1, 3.14t wheels build without it. Side effect: we no longer ship 3.13t wheels, which aligns with upstream's migration push to 3.14t. - Drop `thread_unsafe_fixtures = ["capsys", "capfd"]`: pytest-run-parallel marks these automatically. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop re-init concurrency test, document construction as thread-local Per review discussion: the `SCS_init` guard against concurrent re-init of a live instance is not standard practice for CPython extensions — NumPy, SciPy, and similar libraries assume object construction is thread-local and do not lock `__init__`. The `test_reinit_while_solving` test also swallowed exceptions broadly, which hid rather than detected any real race on the `self->work` field. Remove the test and add a docstring note to `SCS.__init__` making the thread-local-construction assumption explicit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Use LinearSolver enum in test_concurrent_direct_and_indirect Missed during the rebase onto master: this test still passed the old `use_indirect=` boolean kwarg, which was removed in #189 in favor of the `linear_solver=scs.LinearSolver.*` enum. This broke the full CI matrix (wheel builds, accelerate builds, free-threading tests, sanitizers) on a single shared failure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Mark test_resolve_auto_* as thread_unsafe These tests patch scs.sys and scs._load_module — module-level state that leaks to other tests running in parallel threads under pytest-run-parallel, causing MagicMock to be substituted for the real scs.SCS class in unrelated tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This was referenced Sep 6, 2026
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the boolean flags (
gpu,mkl,apple_ldl,dense,cudss,use_indirect) with a singlelinear_solverparameter that takes ascs.LinearSolverenum value (also accepts strings).Available solvers:
AUTO,QDLDL,INDIRECT,MKL,ACCELERATE,DENSE,GPU,CUDSS.The default is
AUTO, which auto-detects the best available direct solver:Usage
Implementation
Module selection uses dict dispatch for clean, extensible mapping:
This is a breaking API change: the old boolean flags are removed, not deprecated.
Test plan
linear_solver=APILinearSolverenum value (QDLDL, INDIRECT, MKL, ACCELERATE, DENSE, GPU, CUDSS)linear_solver="qdldl")AUTOdefault works when no solver is specifiedValueError🤖 Generated with Claude Code